class Bar: def __init__(self, name, age): self.name = name self.age = age # def __str__(self): # print("this str") # return "名字是%s age%s" % (self.name, self.age) def __repr__(self): # 轉換字符串,在解釋器中執行 print("thsi repr") return "%s" % self.name test = Bar("alex", 18) print(test) #__str__定義在類內部,必須返回一個字符串類型, #__repr__定義在類內部,必須返回一個字符串類型, #打印由這個類產生的對象時,會觸發執行__str__,若是沒有__str__會觸發__repr__
class Bar: pass class Foo(Bar): pass obj=Foo() print(isinstance(obj,Foo)) print(Foo.__bases__) print(issubclass(Foo,Bar)) """ True (<class '__main__.Bar'>,) True """
class Foo: def __init__(self,x): self.name=x def __getattr__(self, item): print('----> from getattr:你找的屬性不存在') def __setattr__(self, key, value): #這的key類型是str print('----> from setattr') # if not isinstance(value,str): # raise TypeError('must be str') #setattr(self,key,value)和 self.key=value #這就無限遞歸了,你好好想一想 self.__dict__[key]=value def __delattr__(self, item): #這的item類型是str print('----> from delattr') # delattr(self,item)和 del self.item #這就無限遞歸了 self.__dict__.pop(item) # __setattr__添加/修改屬性會觸發它的執行 f1 = Foo('alex') #設置屬性 觸發__setattr__ print(f1.__dict__) # 由於你重寫了__setattr__,凡是賦值操做都會觸發它的運行,你啥都沒寫,就是根本沒賦值,除非你直接操做屬性字典,不然永遠沒法賦值 f1.z = 3 # 添加屬性 觸發__setattr__ print(f1.__dict__) setattr(f1,'nn',55) #添加屬性 觸發__setattr__ print(f1.__dict__) f1.__dict__['a'] = 3 # 能夠直接修改屬性字典,來完成添加/修改屬性的操做,這樣的操做不觸發__setattr__ print(f1.__dict__) # __delattr__刪除屬性的時候會觸發 del f1.a #觸發__delattr__ print(f1.__dict__) delattr(f1,'nn') #觸發__delattr__ print(f1.__dict__) f1.__dict__.pop('z') #不觸發__delattr__ print(f1.__dict__) # __getattr__只有在使用點調用屬性且屬性不存在的時候纔會觸發 f1.xxxxxx
class Foo: def __init__(self,x): self.x=x def __getattr__(self, item): print('執行的是我') # return self.__dict__[item] f1=Foo(10) print(f1.x) f1.xxxxxx #不存在的屬性訪問,觸發__getattr__ """ 10 執行的是我 """ class Foo: def __init__(self,x): self.x=x def __getattribute__(self, item): print('無論是否存在,我都會執行') f1=Foo(10) f1.x f1.xxxxxx """ 無論是否存在,我都會執行 無論是否存在,我都會執行 """ class Foo: def __init__(self,x): self.x=x def __getattr__(self, item): print('執行的是我') # return self.__dict__[item] def __getattribute__(self, item): print('無論是否存在,我都會執行') raise AttributeError('哈哈') f1=Foo(10) f1.x f1.xxxxxx """ 無論是否存在,我都會執行 執行的是我 無論是否存在,我都會執行 執行的是我 """
包裝:python爲你們提供了標準數據類型,以及豐富的內置方法,其實在不少場景下咱們都須要基於標準數據類型來定製咱們本身的數據類型,新增/改寫方法,這就用到了咱們剛學的繼承/派生知識(其餘的標準類型都可以經過下面的方式進行二次加工)javascript
class List(list): #繼承list全部的屬性,也能夠派生出本身新的,好比append和mid def append(self, p_object): ' 派生本身的append:加上類型檢查' if not isinstance(p_object,int): raise TypeError('must be int') super().append(p_object) @property def mid(self): '新增本身的屬性' index=len(self)//2 return self[index] l=List([1,2,3,4]) print(l) l.append(5) print(l) # l.append('1111111') #報錯,必須爲int類型 print(l.mid) #其他的方法都繼承list的 l.insert(0,-123) print(l) l.clear() print(l)
受權:受權是包裝的一個特性, 包裝一個類型一般是對已存在的類型的一些定製,這種作法能夠新建,修改或刪除原有產品的功能。其它的則保持原樣。受權的過程,便是全部更新的功能都是由新類的某部分來處理,但已存在的功能就受權給對象的默認屬性。 實現受權的關鍵點就是覆蓋__getattr__方法java
import time class FileHandle: def __init__(self,filename,mode='r',encoding='utf-8'): self.file=open(filename,mode,encoding=encoding) def write(self,line): t=time.strftime('%Y-%m-%d %T') self.file.write('%s %s' %(t,line)) def __getattr__(self, item): return getattr(self.file,item) f1=FileHandle('b.txt','w+') f1.write('你好啊') f1.seek(0) print(f1.read()) f1.close()
class List: def __init__(self,x): self.seq=list(x) def append(self,value): if not isinstance(value,str): raise TypeError('must be str') self.seq.append(value) @property def mid(self): index=len(self.seq)//2 return self.seq[index] def __getattr__(self, item): return getattr(self.seq,item) def __str__(self): return str(self.seq) l=List([1,2,3]) l.append('1') print(l.mid) l.insert(0,123123123123123123123123123) # print(l.seq) print(l)
class Foo: def __init__(self,name): self.name=name # 參數類型是字符串 def __getitem__(self, item): print('getitem',type(item)) print(self.__dict__[item]) def __getattr__(self, item): print('----> from getattr:你找的屬性不存在') def __setitem__(self, key, value): print('setitem',type(key)) self.__dict__[key]=value def __setattr__(self, key, value): print('----> from setattr',type(key)) self.__dict__[key] = value def __delitem__(self, key): print('del obj[key]時,我執行',type(key)) self.__dict__.pop(key) def __delattr__(self, item): print('----> from delattr del obj.key時,我執行',type(item)) self.__dict__.pop(item) #觸發__setattr__ f1=Foo('sb') f1.gender='male' setattr(f1,'level','high') #觸發__setitem__ f1['age']=18 f1['age1']=19 #觸發__delattr__ del f1.age1 delattr(f1,'level') #觸發__delitem__ del f1['age'] #觸發__getattr__ f1.xxxxxx #觸發__getitem__ f1['name'] #什麼都不觸發 f1.__dict__['test']='aa' f1.__dict__['test'] f1.__dict__.pop('test') print(f1.__dict__)
# 和生成實例化對象的內容相對應 date_dic={ 'ymd':'{0.year}:{0.month}:{0.day}', 'dmy':'{0.day}/{0.month}/{0.year}', 'mdy':'{0.month}-{0.day}-{0.year}', } class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day def __format__(self, format_spec): # format_spec指定格式化的類型 if not format_spec or format_spec not in date_dic: format_spec='ymd' fmt=date_dic[format_spec] # 經過date_dic字典格式化成相應格式的類型 return fmt.format(self) #把對象傳入,格式化的內容 d1=Date(2016,12,29) print(format(d1)) print('{:mdy}'.format(d1)) """ 2016-12-26 20161226 """
class Poo: __slots__ = 'x' p1 = Poo() p1.x = 1 # p1.y = 2 # 報錯 print(p1.__slots__) #打印x p1再也不有__dict__ class Bar: __slots__ = ['x', 'y'] n = Bar() n.x, n.y = 1, 2 # n.z = 3 # 報錯 print(n.__slots__) #打印['x', 'y'] class Foo: __slots__=['name','age'] f1=Foo() f1.name='alex' f1.age=18 print(f1.__slots__) #['name', 'age'] f2=Foo() f2.name='egon' f2.age=19 print(f2.__slots__) #['name', 'age'] print(Foo.__dict__)#f1與f2都沒有屬性字典__dict__了,統一歸__slots__管,節省內存 """ { 'age': <member 'age' of 'Foo' objects>, 'name': <member 'name' of 'Foo' objects>, '__module__': '__main__', '__doc__': None, '__slots__': ['name', 'age']}ame', 'age'] } """
class Foo: def __init__(self,start,stop): self.num=start self.stop=stop def __iter__(self): return self def __next__(self): if self.num >= self.stop: raise StopIteration n=self.num self.num+=1 return n f=Foo(1,5) from collections import Iterable,Iterator print(isinstance(f,Iterator)) for i in Foo(1,5): print(i) """ True 1 2 3 4 """
class Range: def __init__(self,start,end,step): self.start=start self.end=end self.step=step def __next__(self): if self.start >= self.end: raise StopIteration x=self.start self.start+=self.step return x def __iter__(self): return self for i in Range(1,7,2): print(i) #1 3 5
class Fib: def __init__(self): self._a=0 self._b=1 def __iter__(self): return self def __next__(self): self._a,self._b=self._b,self._a + self._b return self._a f1=Fib() print(f1.__next__()) print(next(f1)) print(next(f1)) for i in f1: if i > 100: break print('%s ' %i,end='') ''' 1 1 2 3 5 8 13 21 34 55 89 '''
class Foo: #我是描述信息 pass class Bar(Foo): pass print(Bar.__doc__) #None 該屬性沒法繼承給子類
#test.py class C: def __init__(self): self.name = '測試' #aa.py from test import C class Foo: pass f1 = Foo() print(f1.__module__) #__main__ print(f1.__class__) #<class '__main__.Foo'> c1 = C() print(c1.__module__) #test 即:輸出模塊 print(c1.__class__) #<class 'test.C'> 即:輸出類
class Foo: def __del__(self): print('執行我啦') f1=Foo() del f1 print('------->') """ 執行我啦 -------> """ class Foo: def __del__(self): print('執行我啦') f1=Foo() # del f1 print('------->') """ -------> 執行我啦 """
""" 典型的應用場景: 建立數據庫類,用該類實例化出數據庫連接對象,對象自己是存放於用戶空間內存中,而連接則是由操做系統管理的,存放於內核空間內存中 當程序結束時,python只會回收本身的內存空間,即用戶態內存,而操做系統的資源則沒有被回收,這就須要咱們定製__del__, 在對象被刪除前向操做系統發起關閉數據庫連接的系統調用,回收資源 這與文件處理是一個道理: f=open('a.txt') #作了兩件事,在用戶空間拿到一個f變量,在操做系統內核空間打開一個文件 del f #只回收用戶空間的f,操做系統的文件還處於打開狀態 #因此咱們應該在del f以前保證f.close()執行,即使是沒有del,程序執行完畢也會自動del清理資源,因而文件操做的正確用法應該是 f=open('a.txt') #讀寫... f.close() #不少狀況下你們都容易忽略f.close,這就用到了with上下文管理 """ import time class Open: def __init__(self,filepath,mode='r',encode='utf-8'): self.f=open(filepath,mode=mode,encoding=encode) def write(self): pass def __getattr__(self, item): return getattr(self.f,item) def __del__(self): print('----->del') self.f.close() ## f=Open('a.txt','w') f1=f del f #f1=f 引用計數不爲零 print('=========>') """ =========> ----->del """
""" with open('a.txt') as f: '代碼塊' 上述叫作上下文管理協議,即with語句,爲了讓一個對象兼容with語句,必須在這個對象的類中聲明__enter__和__exit__方法 __exit__的運行完畢就表明了整個with語句的執行完畢 """ class Open: def __init__(self,name): self.name=name def __enter__(self): print('出現with語句,對象的__enter__被觸發,有返回值則賦值給as聲明的變量') return self def __exit__(self, exc_type, exc_val, exc_tb): print('with中代碼塊執行完畢時執行我啊') with Open('a.txt') as f: print('=====>執行代碼塊') # print(f,f.name) #__enter__方法的返回值賦值給as聲明的變量 """ 出現with語句,對象的__enter__被觸發,有返回值則賦值給as聲明的變量 =====>執行代碼塊 with中代碼塊執行完畢時執行我啊 """ #__exit__()中的三個參數分別表明異常類型,異常值和追溯信息,with語句中代碼塊出現異常,則with後的代碼都沒法執行 class Open: def __init__(self,name): self.name=name def __enter__(self): print('出現with語句,對象的__enter__被觸發,有返回值則賦值給as聲明的變量') def __exit__(self, exc_type, exc_val, exc_tb): print('with中代碼塊執行完畢時執行我啊') print(exc_type) print(exc_val) print(exc_tb) with Open('a.txt') as f: print('=====>執行代碼塊') raise AttributeError('***着火啦,救火啊***') #遇到raise,with裏代碼就結束,with代碼塊裏raise後面的代碼就不會執行,with代碼結束就會執行__exit__方法 print('11111111') ####永遠不會打印 print('0'*100) #------------------------------->不會執行 #若是__exit()返回值爲True,那麼異常會被清空,就好像啥都沒發生同樣,with後的語句正常執行 class Open: def __init__(self,name): self.name=name def __enter__(self): print('出現with語句,對象的__enter__被觸發,有返回值則賦值給as聲明的變量') def __exit__(self, exc_type, exc_val, exc_tb): print('with中代碼塊執行完畢時執行我啊') print(exc_type) print(exc_val) print(exc_tb) return True #有返回值 with Open('a.txt') as f: print('=====>執行代碼塊') raise AttributeError('***着火啦,救火啊***') print('11111111') ####永遠不會打印 print('0'*100) #------------------------------->會執行 #__exit__方法返回布爾值爲true的值時,with代碼塊後面的代碼會執行
class Open: def __init__(self,filepath,mode,encode='utf-8'): self.f=open(filepath,mode=mode,encoding=encode) self.filepath=filepath self.mode=mode self.encoding=encode def write(self,line): print('write') self.f.write(line) def __getattr__(self, item): return getattr(self.f,item) def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.f.close() return True with Open('a.txt','w') as f: #f=Open('a.txt','w') f.write('11\n') f.write('22\n') print(sssssssssssssss) #拋出異常,交給__exit__處理 f.write('33\n') # 這不執行 print('aaaaa')#執行
#被property裝飾的屬性會優先於對象的屬性被使用,只有在屬性sex定義property後才能定義sex.setter,sex.deleter class People: def __init__(self,name,SEX): self.name=name # self.__sex=SEX #這不調用@sex.setter def sex(self,value):方法 由於設置的是__sex 不是 sex,或者說__sex沒有被@property裝飾 self.sex=SEX #①由於sex被@property裝飾,因此self.sex=SEX是去找@sex.setter def sex(self,value): 方法,而不是給對象賦值 @property def sex(self): return self.__sex #p1.__sex @sex.setter def sex(self,value): print('...') if not isinstance(value,str): raise TypeError('性別必須是字符串類型') self.__sex=value #② male給了value , self.__sex='male' @sex.deleter #del p1.sex的時候調用 def sex(self): del self.__sex #del p1.__sex p1=People('alex','male') #會調用 @sex.setter def sex(self,value): 方法是由於__init__中 self.sex=SEX 是給sex設置值了 p1.sex='female' #會調用@sex.setter def sex(self,value): 方法 del p1.sex print(p1.sex) #AttributeError: 'People' object has no attribute '_People__sex'
class Foo: def get_AAA(self): print('get的時候運行我啊') def set_AAA(self,value): print('set的時候運行我啊') def delete_AAA(self): print('delete的時候運行我啊') AAA=property(get_AAA,set_AAA,delete_AAA) #內置property三個參數與get,set,delete一一對應 f1=Foo() f1.AAA f1.AAA='aaa' del f1.AAA """ get的時候運行我啊 set的時候運行我啊 delete的時候運行我啊 """
class Goods: def __init__(self): # 原價 self.original_price = 100 # 折扣 self.discount = 0.8 @property def price(self): # 實際價格 = 原價 * 折扣 new_price = self.original_price * self.discount return new_price @price.setter def price(self, value): self.original_price = value @price.deleter def price(self): del self.original_price obj = Goods() print(obj.price) # 80.0 obj.price = 200 # 修改商品原價 print(obj.price) # 160.0 del obj.price # 刪除商品原價
#實現類型檢測功能 #第一關: class People: def __init__(self,name): self.name=name @property def name(self): return self.name p1=People('alex') #property自動實現了set和get方法屬於數據描述符,比實例屬性優先級高,因此你這寫會觸發property內置的set,拋出異常 #第二關:修訂版 class People: def __init__(self,name): self.name=name #實例化就觸發property @property def name(self): # return self.name #無限遞歸 print('get------>') return self.DouNiWan @name.setter def name(self,value): print('set------>') self.DouNiWan=value @name.deleter def name(self): print('delete------>') del self.DouNiWan p1=People('alex') #self.name實際是存放到self.DouNiWan裏 print(p1.name) print(p1.name) print(p1.name) print(p1.__dict__) p1.name='egon' print(p1.__dict__) del p1.name print(p1.__dict__) #第三關:加上類型檢查 class People: def __init__(self,name): self.name=name #實例化就觸發property @property def name(self): # return self.name #無限遞歸 print('get------>') return self.DouNiWan @name.setter def name(self,value): print('set------>') if not isinstance(value,str): raise TypeError('必須是字符串類型') self.DouNiWan=value @name.deleter def name(self): print('delete------>') del self.DouNiWan p1=People('alex') #self.name實際是存放到self.DouNiWan裏 p1.name=1
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print('__call__') obj = Foo() # 執行 __init__ obj() # 執行 __call__
class MyType(type): def __call__(cls, *args, **kwargs): print(cls) obj = cls.__new__(cls, *args, **kwargs) print('在這裏面..') print('==========================') obj.__init__(*args, **kwargs) return obj class Foo(metaclass=MyType): def __init__(self): self.name = 'alex' print('123') f = Foo() #調用 print(f.name) """ <class '__main__.Foo'> 在這裏面.. ========================== 123 alex """
def test(x:int,y:int)->int: return x+y print(test.__annotations__) #{'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}
class Foo: def __get__(self, instance, owner): print('觸發get') def __set__(self, instance, value): print('觸發set') def __delete__(self, instance): print('觸發delete') #包含這三個方法的新式類稱爲描述符,由這個類產生的實例進行屬性的調用/賦值/刪除,並不會觸發這三個方法 f1=Foo() f1.name='egon' f1.name del f1.name #疑問:什麼時候,何地,會觸發這三個方法的執行 #描述符分兩種 #1:數據描述符:至少實現了__get__()和__set__() class Foo: def __set__(self, instance, value): print('set') def __get__(self, instance, owner): print('get') #2:非數據描述符:沒有實現__set__() class Foo: def __get__(self, instance, owner): print('get')
#描述符Str class Str: def __get__(self, instance, owner): print('Str調用') def __set__(self, instance, value): print('Str設置...') def __delete__(self, instance): print('Str刪除...') #描述符Int class Int: def __get__(self, instance, owner): print('Int調用') def __set__(self, instance, value): print('Int設置...') def __delete__(self, instance): print('Int刪除...') class People: name=Str() age=Int() def __init__(self,name,age): #name被Str類代理,age被Int類代理, self.name=name self.age=age #何地?:定義成另一個類的類屬性 #什麼時候?:且看下列演示 p1=People('alex',18) #描述符Str的使用 p1.name p1.name='egon' del p1.name #描述符Int的使用 p1.age p1.age=18 del p1.age #咱們來瞅瞅到底發生了什麼 print(p1.__dict__) print(People.__dict__) #補充 print(type(p1) == People) #type(obj)實際上是查看obj是由哪一個類實例化來的 print(type(p1).__dict__ == People.__dict__)
#描述符Str class Str: def __get__(self, instance, owner): print('Str調用') def __set__(self, instance, value): print('Str設置...') def __delete__(self, instance): print('Str刪除...') class People: name=Str() def __init__(self,name,age): #name被Str類代理,age被Int類代理, self.name=name self.age=age p1=People('egon',18) #若是描述符是一個數據描述符(即有__get__又有__set__),那麼p1.name的調用與賦值都是觸發描述符的操做,於p1自己無關了,至關於覆蓋了實例的屬性 p1.name='egonnnnnn' p1.name print(p1.__dict__)#實例的屬性字典中沒有name,由於name是一個數據描述符,優先級高於實例屬性,查看/賦值/刪除都是跟描述符有關,與實例無關了 del p1.name
class Foo: def __set__(self, instance, value): print('set') def __get__(self, instance, owner): print('get') class Room: name=Foo() def __init__(self,name,width,length): self.name=name self.width=width self.length=length #name是一個數據描述符,由於name=Foo()而Foo實現了get和set方法,於是比實例屬性有更高的優先級 #對實例的屬性操做,觸發的都是描述符的 r1=Room('廁所',1,1) r1.name r1.name='廚房' class Foo: def __get__(self, instance, owner): print('get') class Room: name=Foo() def __init__(self,name,width,length): self.name=name self.width=width self.length=length #name是一個非數據描述符,由於name=Foo()而Foo沒有實現set方法,於是比實例屬性有更低的優先級 #對實例的屬性操做,觸發的都是實例本身的 r1=Room('廁所',1,1) r1.name r1.name='廚房'
class Str: def __init__(self,name): self.name=name def __get__(self, instance, owner): print('get--->',instance,owner) return instance.__dict__[self.name] def __set__(self, instance, value): print('set--->',instance,value) instance.__dict__[self.name]=value def __delete__(self, instance): print('delete--->',instance) instance.__dict__.pop(self.name) class People: name=Str('name') def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1=People('egon',18,3231.3) #調用 print(p1.__dict__) p1.name #賦值 print(p1.__dict__) p1.name='egonlin' print(p1.__dict__) #刪除 print(p1.__dict__) del p1.name print(p1.__dict__)
class Str: def __init__(self,name): self.name=name def __get__(self, instance, owner): print('get--->',instance,owner) return instance.__dict__[self.name] def __set__(self, instance, value): print('set--->',instance,value) instance.__dict__[self.name]=value def __delete__(self, instance): print('delete--->',instance) instance.__dict__.pop(self.name) class People: name=Str('name') def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary #疑問:若是我用類名去操做屬性呢 People.name #報錯,錯誤的根源在於類去操做屬性時,會把None傳給instance #修訂__get__方法 class Str: def __init__(self,name): self.name=name def __get__(self, instance, owner): print('get--->',instance,owner) if instance is None: return self return instance.__dict__[self.name] def __set__(self, instance, value): print('set--->',instance,value) instance.__dict__[self.name]=value def __delete__(self, instance): print('delete--->',instance) instance.__dict__.pop(self.name) class People: name=Str('name') def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary print(People.name) #完美,解決
class Str: def __init__(self,name,expected_type): self.name=name self.expected_type=expected_type def __get__(self, instance, owner): print('get--->',instance,owner) if instance is None: return self return instance.__dict__[self.name] def __set__(self, instance, value): print('set--->',instance,value) if not isinstance(value,self.expected_type): #若是不是指望的類型,則拋出異常 raise TypeError('Expected %s' %str(self.expected_type)) instance.__dict__[self.name]=value def __delete__(self, instance): print('delete--->',instance) instance.__dict__.pop(self.name) class People: name=Str('name',str) #新增類型限制str def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1=People(123,18,3333.3)#傳入的name因不是字符串類型而拋出異常
def decorate(cls): print('類的裝飾器開始運行啦------>') return cls @decorate #無參:People=decorate(People) class People: def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1=People('egon',18,3333.3)
def typeassert(**kwargs): def decorate(cls): print('類的裝飾器開始運行啦------>',kwargs) return cls return decorate @typeassert(name=str,age=int,salary=float) #有參:1.運行typeassert(...)返回結果是decorate,此時參數都傳給kwargs 2.People=decorate(People) class People: def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1=People('egon',18,3333.3)
class ClassMethod: def __init__(self,func): self.func=func def __get__(self, instance, owner): #類來調用,instance爲None,owner爲類自己,實例來調用,instance爲實例,owner爲類自己, def feedback(): print('在這裏能夠加功能啊...') return self.func(owner) return feedback class People: name='linhaifeng' @ClassMethod # say_hi=ClassMethod(say_hi) def say_hi(cls): print('你好啊,帥哥 %s' %cls.name) People.say_hi() p1=People() p1.say_hi() #疑問,類方法若是有參數呢,好說,好說 class ClassMethod: def __init__(self,func): self.func=func def __get__(self, instance, owner): #類來調用,instance爲None,owner爲類自己,實例來調用,instance爲實例,owner爲類自己, def feedback(*args,**kwargs): print('在這裏能夠加功能啊...') return self.func(owner,*args,**kwargs) return feedback class People: name='linhaifeng' @ClassMethod # say_hi=ClassMethod(say_hi) def say_hi(cls,msg): print('你好啊,帥哥 %s %s' %(cls.name,msg)) People.say_hi('你是那偷心的賊') p1=People() p1.say_hi('你是那偷心的賊')
class StaticMethod: def __init__(self,func): self.func=func def __get__(self, instance, owner): #類來調用,instance爲None,owner爲類自己,實例來調用,instance爲實例,owner爲類自己, def feedback(*args,**kwargs): print('在這裏能夠加功能啊...') return self.func(*args,**kwargs) return feedback class People: @StaticMethod# say_hi=StaticMethod(say_hi) def say_hi(x,y,z): print('------>',x,y,z) People.say_hi(1,2,3) p1=People() p1.say_hi(4,5,6)
#type元類,是全部類的類,利用type模擬class關鍵字的建立類的過程 class Bar2: x=1 def run(self): print('%s is runing' %self.name) print(Bar2.__bases__) print(Bar2.__dict__) #用type模擬Bar2 def run(self): print('%s is runing' %self.name) class_name='Bar' bases=(object,) class_dic={ 'x':1, 'run':run } Bar=type(class_name,bases,class_dic) print(Bar) #<class '__main__.Bar'> print(type(Bar)) #<class 'type'> print(Bar.__bases__) print(Bar.__dict__) #就是新建了一個空類 class Spam2: pass print(Spam2) print(Spam2.__bases__) print(Spam2.__dict__) #用type模擬Spam2 Spam=type('Spam',(),{}) print(Spam) print(Spam.__bases__) print(Spam.__dict__)
class Poo(metaclass=type): #就是執行了 type('Poo',(object,),{'x':1,'run':...}) x=1 def run(self): print('running') #__init__ class Mymeta(type): def __init__(self,class_name,class_bases,class_dic): #這的self就是Foo類 for key in class_dic: if not callable(class_dic[key]):continue if not class_dic[key].__doc__: raise TypeError('你沒寫註釋,趕忙去寫') # type.__init__(self,class_name,class_bases,class_dic) class Foo(metaclass=Mymeta): #就是執行了 Foo=Mymeta('Foo',(object,),{'x':1,'run':...}) x=1 def run(self): 'run function' print('running') #__call__ class Mymeta(type): def __init__(self,class_name,class_bases,class_dic): pass def __call__(self, *args, **kwargs):# print(self) 這的self就是Foo類 obj=self.__new__(self) #建一個空對象 self.__init__(obj,*args,**kwargs) #注意參數obj obj.name='tom' return obj #把對象返回 class Foo(metaclass=Mymeta): x=1 def __init__(self,name): self.name=name #obj.name='tom' def run(self): print('running') f=Foo('tom') print(f) print(f.name)
""" exec:3個參數 參數 1:包含一系列python代碼的字符串 參數 2:全局做用域(字典形式),若是不指定,默認爲globals() 參數 3:局部做用域(字典形式),若是不指定,默認爲locals() 能夠把exec命令的執行當成是一個函數的執行,會將執行期間產生的名字存放於局部名稱空間中 """ g={ 'x':1, 'y':2 } l={} exec(''' global x,z x=100 z=200 m=300 ''',g,l) print(g) #{'x': 100, 'y': 2,'z':200,......} print(l) #{'m': 300}
class Foo: def __call__(self, *args, **kwargs): print(self) print(args) print(kwargs) obj=Foo() #一、要想讓obj這個對象變成一個可調用的對象,須要在該對象的類中定義一個方法__call__方法,該方法會在調用對象時自動觸發 #二、調用obj的返回值就是__call__方法的返回值 res=obj(1,2,3,x=1,y=2) """ <__main__.Foo object at 0x0000000000B0BEB8> (1, 2, 3) {'x': 1, 'y': 2} """
class OldboyTeacher(object): school='oldboy' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the oldboy to learn Python' %self.name) t1=OldboyTeacher('egon',18) print(type(t1)) #查看對象t1的類是<class '__main__.OldboyTeacher'> print(type(OldboyTeacher)) # 結果爲<class 'type'>,證實是調用了type這個元類而產生的OldboyTeacher,即默認的元類爲type """ 一: python中一切皆爲對象。 全部的對象都是實例化或者說調用類而獲得的(調用類的過程稱爲類的實例化),好比對象t1是調用類OldboyTeacher獲得的 元類-->實例化-->類OldboyTeacher-->實例化-->對象t1 二: class關鍵字建立類的流程分析: 用class關鍵字定義的類自己也是一個對象,負責產生該對象的類稱之爲元類(元類能夠簡稱爲類的類),內置的元類爲type class關鍵字在幫咱們建立類時,必然幫咱們調用了元類OldboyTeacher=type(...),那調用type時傳入的參數是什麼呢? 必然是類的關鍵組成部分,一個類有三大組成部分,分別是 一、類名class_name='OldboyTeacher' 二、基類們class_bases=(object,) 三、類的名稱空間class_dic,類的名稱空間是執行類體代碼而獲得的 調用type時會依次傳入以上三個參數 綜上,class關鍵字幫咱們建立一個類應該細分爲如下四個過程: 一、拿到類名:class_name='OldboyTeacher' 二、拿到類的基類們:class_bases=(object,) 三、執行類體代碼,拿到類的名稱空間:class_dic={...} 四、調用元類獲得類:OldboyTeacher=type(class_name,class_bases,class_dic) 三: 自定義元類控制類OldboyTeacher的建立: 一個類沒有聲明本身的元類,默認他的元類就是type,除了使用內置元類type, 咱們也能夠經過繼承type來自定義元類,而後使用metaclass關鍵字參數爲一個類指定元類 """ class Mymeta(type): #只有繼承了type類才能稱之爲一個元類,不然就是一個普通的自定義類 pass class OldboyTeacher(object,metaclass=Mymeta): # OldboyTeacher=Mymeta('OldboyTeacher',(object),{...}) school='oldboy' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the oldboy to learn Python' %self.name) """ 四: 自定義元類能夠控制類的產生過程,類的產生過程其實就是元類的調用過程, 即OldboyTeacher=Mymeta('OldboyTeacher',(object),{...}), 調用Mymeta會先產生一個空對象OldoyTeacher, 而後連同調用Mymeta括號內的參數一同傳給Mymeta下的__init__方法,完成初始化,因而咱們能夠 """ class Mymeta(type): #只有繼承了type類才能稱之爲一個元類,不然就是一個普通的自定義類 def __init__(self,class_name,class_bases,class_dic): # print(self) #<class '__main__.OldboyTeacher'> # print(class_bases) #(<class 'object'>,) # print(class_dic) #{'__module__': '__main__', '__qualname__': 'OldboyTeacher', 'school': 'oldboy', '__init__': <function OldboyTeacher.__init__ at 0x102b95ae8>, 'say': <function OldboyTeacher.say at 0x10621c6a8>} super(Mymeta, self).__init__(class_name, class_bases, class_dic) # 重用父類的功能 if class_name.islower(): raise TypeError('類名%s請修改成駝峯體' %class_name) if '__doc__' not in class_dic or len(class_dic['__doc__'].strip(' \n')) == 0: raise TypeError('類中必須有文檔註釋,而且文檔註釋不能爲空') class OldboyTeacher(object,metaclass=Mymeta): # OldboyTeacher=Mymeta('OldboyTeacher',(object),{...}) """ 類OldboyTeacher的文檔註釋 """ school='oldboy' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the oldboy to learn Python' %self.name) """ 五: 自定義元類控制類OldboyTeacher的調用: 儲備知識:__call__ 調用一個對象,就是觸發對象所在類中的__call__方法的執行,若是把OldboyTeacher也當作一個對象, 那麼在OldboyTeacher這個對象的類中也必然存在一個__call__方法 """ class Mymeta(type): #只有繼承了type類才能稱之爲一個元類,不然就是一個普通的自定義類 def __call__(self, *args, **kwargs): print(self) #<class '__main__.OldboyTeacher'> print(args) #('egon', 18) print(kwargs) #{} return 123 class OldboyTeacher(object,metaclass=Mymeta): school='oldboy' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the oldboy to learn Python' %self.name) # 調用OldboyTeacher就是在調用OldboyTeacher類中的__call__方法 # 而後將OldboyTeacher傳給self,溢出的位置參數傳給*,溢出的關鍵字參數傳給** # 調用OldboyTeacher的返回值就是調用__call__的返回值 t1=OldboyTeacher('egon',18) print(t1) #123 """ 六: 默認地,調用t1=OldboyTeacher('egon',18)會作三件事 一、產生一個空對象obj 二、調用__init__方法初始化對象obj 三、返回初始化好的obj 對應着,OldboyTeacher類中的__call__方法也應該作這三件事 """ class Mymeta(type): #只有繼承了type類才能稱之爲一個元類,不然就是一個普通的自定義類 def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'> #一、調用__new__產生一個空對象obj obj=self.__new__(self) # 此處的self是類OldoyTeacher,必須傳參,表明建立一個OldboyTeacher的對象obj #二、調用__init__初始化空對象obj self.__init__(obj,*args,**kwargs) #三、返回初始化好的對象obj return obj class OldboyTeacher(object,metaclass=Mymeta): school='oldboy' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the oldboy to learn Python' %self.name) t1=OldboyTeacher('egon',18) print(t1.__dict__) #{'name': 'egon', 'age': 18} """ 七: 上例的__call__至關於一個模板,咱們能夠在該基礎上改寫__call__的邏輯從而控制調用OldboyTeacher的過程, 好比將OldboyTeacher的對象的全部屬性都變成私有的 """ class Mymeta(type): #只有繼承了type類才能稱之爲一個元類,不然就是一個普通的自定義類 def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'> #一、調用__new__產生一個空對象obj obj=self.__new__(self) # 此處的self是類OldoyTeacher,必須傳參,表明建立一個OldboyTeacher的對象obj #二、調用__init__初始化空對象obj self.__init__(obj,*args,**kwargs) # 在初始化以後,obj.__dict__裏就有值了 obj.__dict__={'_%s__%s' %(self.__name__,k):v for k,v in obj.__dict__.items()} #三、返回初始化好的對象obj return obj class OldboyTeacher(object,metaclass=Mymeta): school='oldboy' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the oldboy to learn Python' %self.name) t1=OldboyTeacher('egon',18) print(t1.__dict__) #{'_OldboyTeacher__name': 'egon', '_OldboyTeacher__age': 18} """ 八: 再看屬性查找: 上例中涉及到查找屬性的問題,好比self.__new__ 結合python繼承的實現原理+元類從新看屬性的查找應該是什麼樣子呢??? 在學習完元類後,其實咱們用class自定義的類也全都是對象(包括object類自己也是元類type的 一個實例,能夠用type(object)查看), 咱們學習過繼承的實現原理,若是把類當成對象去看,將下述繼承應該說成是:對象OldboyTeacher繼承對象Foo,對象Foo繼承對象Bar,對象Bar繼承對象object """ class Mymeta(type): #只有繼承了type類才能稱之爲一個元類,不然就是一個普通的自定義類 n=444 def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'> obj=self.__new__(self) self.__init__(obj,*args,**kwargs) return obj class Bar(object): n=333 class Foo(Bar): n=222 class OldboyTeacher(Foo,metaclass=Mymeta): n=111 school='oldboy' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the oldboy to learn Python' %self.name) print(OldboyTeacher.n) #自下而上依次註釋各個類中的n=xxx,而後從新運行程序,發現n的查找順序爲OldboyTeacher->Foo->Bar->object->Mymeta->type """ 九: 因而屬性查找應該分紅兩層,一層是對象層(基於c3算法的MRO)的查找,另一個層則是類層(即元類層)的查找 #查找順序: #一、先對象層:OldoyTeacher->Foo->Bar->object #二、而後元類層:Mymeta->type 分析下元類Mymeta中__call__裏的self.__new__的查找 """ class Mymeta(type): n=444 def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'> obj=self.__new__(self) print(self.__new__ is object.__new__) #True class Bar(object): n=333 # def __new__(cls, *args, **kwargs): # print('Bar.__new__') class Foo(Bar): n=222 # def __new__(cls, *args, **kwargs): # print('Foo.__new__') class OldboyTeacher(Foo,metaclass=Mymeta): n=111 school='oldboy' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the oldboy to learn Python' %self.name) # def __new__(cls, *args, **kwargs): # print('OldboyTeacher.__new__') OldboyTeacher('egon',18) #觸發OldboyTeacher的類中的__call__方法的執行,進而執行self.__new__開始查找 """ 十: 總結,Mymeta下的__call__裏的self.__new__在OldboyTeacher、Foo、Bar裏都沒有找到__new__的狀況下, 會去找object裏的__new__,而object下默認就有一個__new__,因此即使是以前的類均未實現__new__, 也必定會在object中找到一個,根本不會、也根本不必再去找元類Mymeta->type中查找__new__ 咱們在元類的__call__中也能夠用object.__new__(self)去造對象 但咱們仍是推薦在__call__中使用self.__new__(self)去創造空對象, 由於這種方式會檢索三個類OldboyTeacher->Foo->Bar,而object.__new__則是直接跨過了他們三個 """ class Mymeta(type): #只有繼承了type類才能稱之爲一個元類,不然就是一個普通的自定義類 n=444 def __new__(cls, *args, **kwargs): obj=type.__new__(cls,*args,**kwargs) # 必須按照這種傳值方式 print(obj.__dict__) # return obj # 只有在返回值是type的對象時,纔會觸發下面的__init__ return 123 def __init__(self,class_name,class_bases,class_dic): print('run。。。') class OldboyTeacher(object,metaclass=Mymeta): #OldboyTeacher=Mymeta('OldboyTeacher',(object),{...}) n=111 school='oldboy' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the oldboy to learn Python' %self.name) print(type(Mymeta)) #<class 'type'> """ 產生類OldboyTeacher的過程就是在調用Mymeta,而Mymeta也是type類的一個對象,那麼Mymeta之因此能夠調用,必定是在元類type中有一個__call__方法 該方法中一樣須要作至少三件事: class type: def __call__(self, *args, **kwargs): #self=<class '__main__.Mymeta'> obj=self.__new__(self,*args,**kwargs) # 產生Mymeta的一個對象 self.__init__(obj,*args,**kwargs) return obj """
""" 練習二:在元類中控制自定義的類無需__init__方法 1.元類幫其完成建立對象,以及初始化操做; 2.要求實例化時傳參必須爲關鍵字形式,不然拋出異常TypeError: must use keyword argument 3.key做爲用戶自定義類產生對象的屬性,且全部屬性變成大寫 """ class Mymetaclass(type): # def __new__(cls,name,bases,attrs): # update_attrs={} # for k,v in attrs.items(): # if not callable(v) and not k.startswith('__'): # update_attrs[k.upper()]=v # else: # update_attrs[k]=v # return type.__new__(cls,name,bases,update_attrs) def __call__(self, *args, **kwargs): if args: raise TypeError('must use keyword argument for key function') obj = object.__new__(self) #建立對象,self爲類Foo for k,v in kwargs.items(): obj.__dict__[k.upper()]=v return obj class Chinese(metaclass=Mymetaclass): country='China' tag='Legend of the Dragon' #龍的傳人 def walk(self): print('%s is walking' %self.name) p=Chinese(name='egon',age=18,sex='male') print(p.__dict__)
class Mymeta(type): def __init__(self,class_name,class_bases,class_dic): #控制類Foo的建立 super(Mymeta,self).__init__(class_name,class_bases,class_dic) def __call__(self, *args, **kwargs): #控制Foo的調用過程,即Foo對象的產生過程 obj = self.__new__(self) self.__init__(obj, *args, **kwargs) obj.__dict__={'_%s__%s' %(self.__name__,k):v for k,v in obj.__dict__.items()} return obj class Foo(object,metaclass=Mymeta): # Foo=Mymeta(...) def __init__(self, name, age,sex): self.name=name self.age=age self.sex=sex obj=Foo('egon',18,'male') print(obj.__dict__)
#步驟五:基於元類實現單例模式 # 單例:即單個實例,指的是同一個類實例化屢次的結果指向同一個對象,用於節省內存空間 # 若是咱們從配置文件中讀取配置來進行實例化,在配置相同的狀況下,就不必重複產生對象浪費內存了 #settings.py文件內容以下 HOST='1.1.1.1' PORT=3306 #方式一:定義一個類方法實現單例模式 import settings class Mysql: __instance=None def __init__(self,host,port): self.host=host self.port=port @classmethod def singleton(cls): if not cls.__instance: cls.__instance=cls(settings.HOST,settings.PORT) return cls.__instance obj1=Mysql('1.1.1.2',3306) obj2=Mysql('1.1.1.3',3307) print(obj1 is obj2) #False obj3=Mysql.singleton() obj4=Mysql.singleton() print(obj3 is obj4) #True #方式二:定製元類實現單例模式 import settings class Mymeta(type): def __init__(self,name,bases,dic): #定義類Mysql時就觸發 # 事先先從配置文件中取配置來造一個Mysql的實例出來 self.__instance = object.__new__(self) # 產生對象 self.__init__(self.__instance, settings.HOST, settings.PORT) # 初始化對象 # 上述兩步能夠合成下面一步 # self.__instance=super().__call__(*args,**kwargs) super().__init__(name,bases,dic) def __call__(self, *args, **kwargs): #Mysql(...)時觸發 if args or kwargs: # args或kwargs內有值 obj=object.__new__(self) self.__init__(obj,*args,**kwargs) return obj return self.__instance class Mysql(metaclass=Mymeta): def __init__(self,host,port): self.host=host self.port=port obj1=Mysql() # 沒有傳值則默認從配置文件中讀配置來實例化,全部的實例應該指向一個內存地址 obj2=Mysql() obj3=Mysql() print(obj1 is obj2 is obj3) obj4=Mysql('1.1.1.4',3307) #方式三:定義一個裝飾器實現單例模式 import settings def singleton(cls): #cls=Mysql _instance=cls(settings.HOST,settings.PORT) def wrapper(*args,**kwargs): if args or kwargs: obj=cls(*args,**kwargs) return obj return _instance return wrapper @singleton # Mysql=singleton(Mysql) class Mysql: def __init__(self,host,port): self.host=host self.port=port obj1=Mysql() obj2=Mysql() obj3=Mysql() print(obj1 is obj2 is obj3) #True obj4=Mysql('1.1.1.3',3307) obj5=Mysql('1.1.1.4',3308) print(obj3 is obj4) #False