【目錄】 一、什麼是元類 二、類是如何產生的——關鍵字class創造類的過程 三、如何自定義元類來控制類的產生 四、內置方法 __call__ 五、自定義元類控制類的調用=》類的對象的產生 六、再訪——屬性查找 一、什麼是元類 一切都源自於一句話:一切皆為對象 # 元類就是用來實例化產生類的類# 關 ...
【目錄】
一、什麼是元類
二、類是如何產生的——關鍵字class創造類的過程
三、如何自定義元類來控制類的產生
四、內置方法 __call__
五、自定義元類控制類的調用=》類的對象的產生
六、再訪——屬性查找
一、什麼是元類
一切都源自於一句話:一切皆為對象
# 元類就是用來實例化產生類的類
# 關係:元類---實例化---->類(People)---實例化---->對象(obj)
class People: def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s:%s' %(self.name,self.name)) print(People.__dict__) # 如何得到對象 # obj=調用類() obj=People('egon',18) print(type(obj)) # 如果說類也是對象 # People=調用類(。。。) # 查看內置的元類: # 1、type是內置的元類 # 2、我們用class關鍵字定義的所有的類以及內置的類都是由元類type實例化產生 print(type(People)) print(type(int))
二、類是如何產生的——關鍵字class創造類的過程
類有三大特征:類名,類的基類,類體
類有三大特征: # 1、類名 class_name="People" # 2、類的基類 class_bases=(object,) # 3、執行類體代碼拿到類的名稱空間 class_dic={} class_body=""" def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s:%s' %(self.name,self.name)) """ exec(class_body,{},class_dic) # print(class_dic) # 4、調用元類 People=type(class_name,class_bases,class_dic)
三、如何自定義元類來控制類的產生
class Mymeta(type): # 只有繼承了type類的類才是元類 # 空對象,"People",(),{...} def __init__(self,x,y,z): print('run22222222222....') print(self) # print(x) # print(y) # print(z) # print(y) # if not x.istitle(): # raise NameError('類名的首字母必須大寫啊!!!') # 當前所在的類,調用類時所傳入的參數 def __new__(cls, *args, **kwargs): # 造Mymeta的對象 print('run1111111111.....') # print(cls,args,kwargs) # return super().__new__(cls,*args, **kwargs) return type.__new__(cls,*args, **kwargs) People=Mymeta("People",(object,),{...}) # 調用Mymeta發生三件事,調用Mymeta就是type.__call__ # 1、先造一個空對象=>People,調用Mymeta類內的__new__方法 # 2、調用Mymeta這個類內的__init__方法,完成初始化對象的操作 # 3、返回初始化好的對象 class People(metaclass=Mymeta): def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s:%s' %(self.name,self.name))
強調:
只要是調用類,那麼會一次調用
1、類內的__new__
2、類內的__init__
四、內置方法 __call__
class Foo: def __init__(self,x,y): self.x=x self.y=y # obj,1,2,3,a=4,b=5,c=6 def __call__(self,*args,**kwargs): print('===>',args,kwargs) return 123 obj=Foo(111,222) # print(obj) # obj.__str__ res=obj(1,2,3,a=4,b=5,c=6) # res=obj.__call__() print(res)
應用:如果想讓一個對象可以加括弧調用,需要在該對象的類中添加一個方法__call__
總結:
對象()->類內的__call__
類()->自定義元類內的__call__
自定義元類()->內置元類__call__
五、自定義元類控制類的調用=》類的對象的產生
class Mymeta(type): # 只有繼承了type類的類才是元類 def __call__(self, *args, **kwargs): # 1、Mymeta.__call__函數內會先調用People內的__new__ people_obj=self.__new__(self) # 2、Mymeta.__call__函數內會調用People內的__init__ self.__init__(people_obj,*args, **kwargs) # print('people對象的屬性:',people_obj.__dict__) people_obj.__dict__['xxxxx']=11111 # 3、Mymeta.__call__函數內會返回一個初始化好的對象 return people_obj # 類的產生 # People=Mymeta()=》type.__call__=>幹了3件事 # 1、type.__call__函數內會先調用Mymeta內的__new__ # 2、type.__call__函數內會調用Mymeta內的__init__ # 3、type.__call__函數內會返回一個初始化好的對象 class People(metaclass=Mymeta): def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s:%s' %(self.name,self.name)) def __new__(cls, *args, **kwargs): # 產生真正的對象 return object.__new__(cls) # 類的調用 # obj=People('egon',18) =》Mymeta.__call__=》幹了3件事 # 1、Mymeta.__call__函數內會先調用People內的__new__ # 2、Mymeta.__call__函數內會調用People內的__init__ # 3、Mymeta.__call__函數內會返回一個初始化好的對象 obj1=People('egon',18) obj2=People('egon',18) # print(obj) print(obj1.__dict__) print(obj2.__dict__)
六、再訪——屬性查找
# 屬性查找的原則:對象-》類-》父類
# 切記:父類 不是 元類
class Mymeta(type): n=444 def __call__(self, *args, **kwargs): #self=<class '__main__.StanfordTeacher'> obj=self.__new__(self) # StanfordTeacher.__new__ # obj=object.__new__(self) print(self.__new__ is object.__new__) #True self.__init__(obj,*args,**kwargs) return obj class Bar(object): # n=333 # def __new__(cls, *args, **kwargs): # print('Bar.__new__') pass class Foo(Bar): # n=222 # def __new__(cls, *args, **kwargs): # print('Foo.__new__') pass class StanfordTeacher(Foo,metaclass=Mymeta): # n=111 def __init__(self,name,age): self.name=name self.age=age obj=StanfordTeacher('lili',18) print(obj.__dict__) # print(obj.n) # print(StanfordTeacher.n)
參考資料:
https://zhuanlan.zhihu.com/p/109336845
https://www.cnblogs.com/linhaifeng/articles/6204014.html