1. 程式人生 > >元類metaclass

元類metaclass

add 手動 isp aps pos sel list 方式 base

閱讀目錄

  • 一 知識儲備
  • 二 引子(類也是對象)
  • 三 什麽是元類?
  • 四 創建類的兩種方式
  • 五 自定義元類控制類的行為
  • 六 練習題

一 知識儲備

技術分享圖片
exec:三個參數

參數一:字符串形式的命令

參數二:全局作用域(字典形式),如果不指定,默認為globals()

參數三:局部作用域(字典形式),如果不指定,默認為locals()
技術分享圖片 技術分享圖片exec的使用

二 引子(類也是對象)

class Foo:
    pass

f1=Foo() #f1是通過Foo類實例化的對象

python中一切皆是對象,類本身也是一個對象,當使用關鍵字class的時候,python解釋器在加載class的時候就會創建一個對象(這裏的對象指的是類而非類的實例),因而我們可以將類當作一個對象去使用,同樣滿足第一類對象的概念,可以:

  • 把類賦值給一個變量

  • 把類作為函數參數進行傳遞

  • 把類作為函數的返回值

  • 在運行時動態地創建類

上例可以看出f1是由Foo這個類產生的對象,而Foo本身也是對象,那它又是由哪個類產生的呢?

1 #type函數可以查看類型,也可以用來查看對象的類,二者是一樣的
2 print(type(f1)) # 輸出:<class ‘__main__.Foo‘>     表示,obj 對象由Foo類創建
3 print(type(Foo)) # 輸出:<type ‘type‘>  

三 什麽是元類?

元類是類的類,是類的模板

元類是用來控制如何創建類的,正如類是創建對象的模板一樣,而元類的主要目的是為了控制類的創建行為

元類的實例化的結果為我們用class定義的類,正如類的實例為對象(f1對象是Foo類的一個實例Foo類是 type 類的一個實例)

type是python的一個內建元類,用來直接控制生成類,python中任何class定義的類其實都是type類實例化的對象

技術分享圖片

四 創建類的兩種方式

方式一:使用class關鍵字

技術分享圖片
class Chinese(object):
    country=China
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def talk(self):
        
print(%s is talking %self.name)
技術分享圖片

方式二:就是手動模擬class創建類的過程):將創建類的步驟拆分開,手動去創建

技術分享圖片
#準備工作:

#創建類主要分為三部分

  1 類名

  2 類的父類

  3 類體


#類名
class_name=Chinese
#類的父類
class_bases=(object,)
#類體
class_body="""
country=‘China‘
def __init__(self,name,age):
    self.name=name
    self.age=age
def talk(self):
    print(‘%s is talking‘ %self.name)
"""
技術分享圖片

步驟一(先處理類體->名稱空間):類體定義的名字都會存放於類的名稱空間中(一個局部的名稱空間),我們可以事先定義一個空字典,然後用exec去執行類體的代碼(exec產生名稱空間的過程與真正的class過程類似,只是後者會將__開頭的屬性變形),生成類的局部名稱空間,即填充字典

技術分享圖片
class_dic={}
exec(class_body,globals(),class_dic)


print(class_dic)
#{‘country‘: ‘China‘, ‘talk‘: <function talk at 0x101a560c8>, ‘__init__‘: <function __init__ at 0x101a56668>}
技術分享圖片

步驟二:調用元類type(也可以自定義)來產生類Chinense

技術分享圖片
Foo=type(class_name,class_bases,class_dic) #實例化type得到對象Foo,即我們用class定義的類Foo


print(Foo)
print(type(Foo))
print(isinstance(Foo,type))
‘‘‘
<class ‘__main__.Chinese‘>
<class ‘type‘>
True
‘‘‘
技術分享圖片

我們看到,type 接收三個參數:

  • 第 1 個參數是字符串 ‘Foo’,表示類名

  • 第 2 個參數是元組 (object, ),表示所有的父類

  • 第 3 個參數是字典,這裏是一個空字典,表示沒有定義屬性和方法

補充:若Foo類有繼承,即class Foo(Bar):.... 則等同於type(‘Foo‘,(Bar,),{})

五 自定義元類控制類的行為

#一個類沒有聲明自己的元類,默認他的元類就是type,除了使用元類type,用戶也可以通過繼承type來自定義元類(順便我們也可以瞅一瞅元類如何控制類的行為,工作流程是什麽)
技術分享圖片峰哥5步帶你學會元類

六 練習題

練習一:在元類中控制把自定義類的數據屬性都變成大寫

技術分享圖片
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)

class Chinese(metaclass=Mymetaclass):
    country=China
    tag=Legend of the Dragon #龍的傳人
    def walk(self):
        print(%s is walking %self.name)


print(Chinese.__dict__)
‘‘‘
{‘__module__‘: ‘__main__‘,
 ‘COUNTRY‘: ‘China‘, 
 ‘TAG‘: ‘Legend of the Dragon‘,
 ‘walk‘: <function Chinese.walk at 0x0000000001E7B950>,
 ‘__dict__‘: <attribute ‘__dict__‘ of ‘Chinese‘ objects>,                                         
 ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Chinese‘ objects>,
 ‘__doc__‘: None}
‘‘‘
View Code

練習二:在元類中控制自定義的類無需__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__)
View Code

元類metaclass