1. 程式人生 > 實用技巧 >python面向物件進階

python面向物件進階

類相關的內建函式
isinstance
內建函式
isinstance(obj,cls) 檢查obj是否是cls的物件
class C:pass
a = C()
print(isinstance(a,C)) 返回布林型別

issubclass
issubclass(sub,super)檢查sub是否是super的派生類
class B(C):
pass
print(issubclass(B,C)) 返回布林型別

反射

1 什麼是反射

反射的概念是由Smith在1982年首次提出的,主要是指程式可以訪問、檢測和修改它本身狀態或行為的一種能力(自省)。這一概念的提出很快引發了電腦科學領域關於應用反射性的研究。它首先被程式語言的設計領域所採用,並在Lisp和麵向物件方面取得了成績。

2 python面向物件中的反射:通過字串的形式操作物件相關的屬性。python中的一切事物都是物件(都可以使用反射)

四個可以實現自省的函式

下列方法適用於類和物件(一切皆物件,類本身也是一個物件)

四個方法:hasattr getattr setatte delattr

hasattr 和 getattr 組合使用

import mymodul  # 是自定義模組
if hasattr(mymodul,'Modul_cls'): #判斷是否存在這個類名再執行後面的
print(getattr(mymodul,'Modul_cls')) # 獲取到一個類的物件
#<class 'mymodul.Modul_cls'>

class A:
def func(self):
print('in func')
a = A()
a.name = 'alex'
a.age = 63
A.func(a)
# 反射物件的屬性
ret = getattr(a,'age') # 通過變數名的字串形式取到的值
print(ret)
# 反射物件的方法
print(getattr(a,'func'))
# 反射類的方法
print(getattr(A,'func'))
反射模組的屬性

import mymodul
print(getattr(mymodul,'year'))

# 反射模組的方法
getattr(mymodul,'fangfa')() # 獲取到的是一個函式名加()就是呼叫
# 內建模組也能用

# 反射自己模組中的變數和模組
def func():
print('自己模組名')

import sys
print(sys.modules['__main__']) # 這裡要使用__name__如被其他指令碼匯入後使用用__name才能生效此方法
im = input('>>>')
getattr(sys.modules[__name__],im)()
要反射的函式有引數
import time
time.strftime('%Y-%m-%d %H:%M:%S')
getattr(time,'strftime')('%Y-%m-%d %H:%M:%S')
def qqx():pass

import sys
print(sys.modules['__main__']) #__name__

setattr # 設定修改變數
class D:pass
a = D()
setattr(a,'name','classtor') # 往物件a中新增一個靜態屬性
print(a.name)
setattr(D,'name',123)
print(D.name) # 往類D 中新增靜態屬性

delattr 刪除屬性
delattr(a,'name') # 刪除了物件a屬性中的name 列印類中的屬性 123
print(a.name) # 123
delattr(D,'name')
print(a.name) # 刪除了類的name屬性再列印找不到會報錯
class Foo:
    f = '類的靜態變數'
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say_hi(self):
        print('hi,%s'%self.name)

obj=Foo('egon',73)

#檢測是否含有某屬性
print(hasattr(obj,'name'))
print(hasattr(obj,'say_hi'))

#獲取屬性
n=getattr(obj,'name')
print(n)
func=getattr(obj,'say_hi')
func()

print(getattr(obj,'aaaaaaaa','不存在啊')) #報錯

#設定屬性
setattr(obj,'sb',True)
setattr(obj,'show_name',lambda self:self.name+'sb')
print(obj.__dict__)
print(obj.show_name(obj))

#刪除屬性
delattr(obj,'age')
delattr(obj,'show_name')
delattr(obj,'show_name111')#不存在,則報錯

print(obj.__dict__)
四個方法的其他演示
class Foo(object):
 
    staticField = "old boy"
 
    def __init__(self):
        self.name = 'wupeiqi'
 
    def func(self):
        return 'func'
 
    @staticmethod
    def bar():
        return 'bar'
 
print getattr(Foo, 'staticField')
print getattr(Foo, 'func')
print getattr(Foo, 'bar')
類也是物件
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import sys


def s1():
    print 's1'


def s2():
    print 's2'


this_module = sys.modules[__name__]

hasattr(this_module, 's1')
getattr(this_module, 's2')
反射當前模組成員
#!/usr/bin/env python
# -*- coding:utf-8 -*-

def test():
    print('from the test')

#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
"""
程式目錄:
    module_test.py
    index.py
 
當前檔案:
    index.py
"""

import module_test as obj

#obj.test()

print(hasattr(obj,'test'))

getattr(obj,'test')()
匯入其他模組,利用反射查詢該模組是否存在某個方法
__str__ 和 __reper__ 

改變物件的字串顯示__str__

__repr__ 原型畢露

自定製格式化字串__format__

class A:
# def __str__(self):
# return '執行我啦'
def __init__(self,name,age):
self.name = name
self.age = age
def __repr__(self):
return '執行我啦reper'
def fund(self):
print('執行我啦')
a = A('alex',98)
print(a) # 列印一個物件時,實際是執行物件中__str__方法,沒有返回記憶體地址
# <__main__.A object at 0x01E01DF0>
format_dict={
'nat':'{obj.name}-{obj.addr}-{obj.type}', #學校名-學校地址-學校型別
'tna':'{obj.type}:{obj.name}:{obj.addr}', #學校型別:學校名:學校地址
'tan':'{obj.type}/{obj.addr}/{obj.name}', #學校型別/學校地址/學校名
}
class School:
def __init__(self,name,addr,type):
self.name=name
self.addr=addr
self.type=type
def __repr__(self):
return 'School(%s,%s)' %(self.name,self.addr)
def __str__(self):
return '(%s,%s)' %(self.name,self.addr)
#
# def __format__(self, format_spec):
# # if format_spec
# if not format_spec or format_spec not in format_dict:
# format_spec='nat'
# fmt=format_dict[format_spec]
# return fmt.format(obj=self)

s1=School('oldboy1','北京','私立')
print(s1)
print('from repr: ',repr(s1))
print('from str: ',str(s1))
print(s1)

lst = [1,2,3,4,5] # 例項化列表類方法
print(lst) # 實際呼叫lst下的__str__方法 即obj物件
%s 和 %r
class B:

def __str__(self):
return 'str : class B'

def __repr__(self):
return 'repr : class B'


b=B()
print('%s'%b)
print('%r'%b)

小結:
雙下方法
內建的類方法和內建的函式之間有著千絲萬縷的聯絡
__str__
__repr__ 原型畢露
object 裡有一個__str__,一旦被呼叫,就返回呼叫這個方法的物件的記憶體地址
repr 是 str的備胎
str 不能做 repr 的備胎
print(obj)、%s 、str(obj) 的時候,實際上是內部呼叫了obj.__str__方法,如果str方法有,那麼返回的必定是一個字串,其他會報錯
如果沒有__str__方法,會先找本類中的__repr__方法,再沒有再找父類中的__str__,如沒有就會呼叫object中的__srt__方法會列印一個記憶體地址
repr() 只會找__repr__,如果沒有找父類的

item系列
__getitem__ 和 __setitem__和__delitem__

class Foo:
def __init__(self,name):
self.name=name

def __getitem__(self, item):
print(self.__dict__[item])

def __setitem__(self, key, value):
self.__dict__[key]=value
def __delitem__(self, key):
print('del obj[key]時,我執行')
self.__dict__.pop(key)
def __delattr__(self, item):
print('del obj.key時,我執行')
self.__dict__.pop(item)

f1=Foo('sb')
f1['age']=18
f1['age1']=19
print(f1.__dict__)
del f1.age #執行物件中__delitem__方法
print(f1.__dict__)
del f1['age1'] #物件中有__delattr__方法,可以使用此方式進行刪除
print(f1.__dict__)
f1['name']='alex'
print(f1.__dict__) #物件中有__getitem__方法,可以使用此方式進行刪除

__del__

析構方法,當物件在記憶體中被釋放時,自動觸發執行。

注:此方法一般無須定義,因為Python是一門高階語言,程式設計師在使用時無需關心記憶體的分配和釋放,因為此工作都是交給Python直譯器來執行,所以,解構函式的呼叫是由直譯器在進行垃圾回收時自動觸發執行的。

class A:
def __del__(self): # 解構函式
print('執行我啦')

a = A()
del a # 執行了__del__此方法,有把a這個物件給刪除了
print(a.__dict__) # 所以這裡列印是會報錯哦

__new__ 構造方法:建立一個物件
一個類始終只有一個例項
當你第一個次例項化這個類的時候,就建立一個例項化的物件
當你之後再來例項化的時候,就用之前建立的物件
單列模式
class A:
__instance = False
def __init__(self,name,age):
self.name= name
self.age = age
def __new__(cls, *args, **kwargs):
if cls.__instance:
return cls.__instance
cls.__instance = object.__new__(A) # cls 也可以寫這個
return cls.__instance
egnon nezha 完全相同可以使用 id() == is 檢測
egon = A('egon',38)
egon.cloth = '小花衣'
nezha = A('nezha',25)
print(nezha)
print(egon)
print(nezha.name)
print(egon.name)
print(egon == nezha) # True
print(egon is nezha) # True 對比記憶體地址
__call__

物件後面加括號,觸發執行。注:構造方法的執行是由建立物件觸發的,即:物件 = 類名() ;而對於 __call__ 方法的執行是由物件後加括號觸發的,即:物件() 或者 類()()

class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):

print('__call__')
obj = Foo() # 執行 __init__
obj() # 執行 __call__

with和__enter__,__exit__
with 語句
class A:
def __enter__(self):
print('before')

def __exit__(self, exc_type, exc_val, exc_tb):
print('after')
with A() as a:
print('123')
# before
# 123
# after

with 和檔案操作
# with 和檔案操作
class Myfile:
def __init__(self,path,mode='r',encoding = 'utf-8'):
self.path = path
self.mode = mode
self.encoding = encoding

def __enter__(self):
self.f = open(self.path, mode=self.mode, encoding=self.encoding)
return self.f

def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close()


with Myfile('file',mode='w') as f:
f.write('wahaha') # 在本目錄建立了file檔案並寫入了wahaha

with 和 pickle
import  pickle
class MyPickledump:
def __init__(self,path):
self.path = path

def __enter__(self):
self.f = open(self.path, mode='ab')
return self

def dump(self,content):
pickle.dump(content,self.f)

def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close()

class Mypickleload:
def __init__(self,path):
self.path = path

def __enter__(self):
self.f = open(self.path, mode='rb')
return self

def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close()

def load(self):
return pickle.load(self.f)

def loaditer(self):
while True:
try:
yield self.load()
except EOFError:
break

# with MyPickledump('file') as f:
# f.dump({1,2,3,4}) #以ab模式把1/2/3/4寫入了file檔案內

with Mypickleload('file') as f:
for item in f.loaditer():
print(item)

with 和 pickle 和iter
import pickle
class MyPickledump:
def __init__(self,path):
self.path = path

def __enter__(self):
self.f = open(self.path, mode='ab')
return self

def dump(self,content):
pickle.dump(content,self.f)

def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close()

class Mypickleload:
def __init__(self,path):
self.path = path

def __enter__(self):
self.f = open(self.path, mode='rb')
return self


def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close()

def __iter__(self):
while True:
try:
yield pickle.load(self.f)
except EOFError:
break
# with MyPickledump('file') as f:
# f.dump({1,2,3,4})
with Mypickleload('file') as f:
for item in f:
print(item)
__len__
class A:
def __init__(self):
self.a = 1
self.b = 2

def __len__(self):
return len(self.__dict__)
a = A()
print(len(a)) #求物件的長度是,依賴物件內部的len發放

__hash__ 方法
class H:
def __init__(self,name,sex):
self.name = name
self.sex = sex
def __hash__(self):
return hash(self.name+self.sex)
a = H('egon','男')
b = H('egon','男')
print(hash(a)) # 608824368
print(hash(b)) # 608824368 兩個物件的HASH一致
#
100名字 和 性別相同,就認為是一個物件去重 你年齡不同
set 去重物件
class A:
def __init__(self,name,sex,age):
self.name = name
self.sex = sex
self.age = age
def __hash__(self):
sec = hash(self.name+ self.sex)
return sec
def __eq__(self, other):
if self.name == other.name and self.sex == self.sex:
return True
return False

a = A('egg','男',38)
b = A('egg','男',37)
print(a,b)
print(set((a,b))) # set 方法去重物件時,依賴物件的__hash__ 和 __eq__ 方法

__eq__
class C:
def __init__(self,name):
self.name = name
def __eq__(self, other):
if self.name == other.name:
return True
else:
return False
# print(self,other)

ob1 = C('egg')
ob2 = C('egg')
print(ob1 == ob2) # False

# 紙牌遊戲
import _json
from collections import namedtuple
Card = namedtuple('Card',['rank','suit']) # rank牌面的大小 suit 牌面的花色
ranks = [str(n) for n in range(2,11)] + list('JQKA')
suits = ['紅心','方板','梅花','黑桃']
print(ranks)
lei = [Card(rank,suit) for rank in ranks for suit in suits]
print(lei)

class FranchDeck:
    ranks = [str(n) for n in range(2,11)] + list('JQKA') # 生成2到10 和J到A 字串型別的類表
    suits = ['紅心','方板','梅花','黑桃' ] # 定義一個類表

    def __init__(self):
        self._cards = [Card(rank,suit) for rank in FranchDeck.ranks
                                        for suit in FranchDeck.suits] # 函式巢狀,生成一副撲克牌

    def __len__(self):
        return len(self._cards)

    def __getitem__(self, item):
        return self._cards[item]
    def __setitem__(self, key, value):
        self._cards[key] = value

deck = FranchDeck() # 例項化一個物件
print(deck[0])  # 物件中有__getitem__方法可以使用索引方式檢視屬性
# Card(rank='2', suit='紅心')
from random import choice
print(choice(deck))  # choice取隨機數牌、
# Card(rank='9', suit='方板')
print(choice(deck))
# Card(rank='2', suit='方板')
from random import shuffle
shuffle(deck)
shuffle(deck)
print(deck[:5]) # 洗牌用到的,依賴物件的__len__方法
紙牌遊戲
class FranchDeck:
    ranks = [str(n) for n in range(2,11)] + list('JQKA')
    suits = ['紅心','方板','梅花','黑桃']

    def __init__(self):
        self._cards = [Card(rank,suit) for rank in FranchDeck.ranks
                                        for suit in FranchDeck.suits]

    def __len__(self):
        return len(self._cards)

    def __getitem__(self, item):
        return self._cards[item]

    def __setitem__(self, key, value):
        self._cards[key] = value

deck = FranchDeck()
print(deck[0])
from random import choice
print(choice(deck))
print(choice(deck))

from random import shuffle
shuffle(deck)
print(deck[:5])
紙牌遊戲2
class Person:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex

    def __hash__(self):
        return hash(self.name+self.sex)

    def __eq__(self, other):
        if self.name == other.name and self.sex == other.sex:return True


p_lst = []
for i in range(84):
    p_lst.append(Person('egon',i,'male'))

print(p_lst)
print(set(p_lst))
一道面試題