python的metaclass(元類)
類也是物件
在理解metaclass之前,我們先要掌握python中的類(class)是什麼。
python中類的概念,是借鑑自smalltalk語言。
在大部分語言中,類指的是"描述如何產生一個物件(object)"的一段程式碼,這對於python也是如此。
>>> class ObjectCreator(object):
... pass
...
>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c >
但是,在python中,類遠不止如此,類同時也是物件。
當你遇到關鍵詞class的時候,python就會自動執行產生一個物件。下面的程式碼段中:
>>> class ObjectCreator(object):
... pass
...
python在記憶體中產生了一個名叫做"ObjectCreator"的物件。這個物件(類)自身擁有產生物件(例項instance)的能力。 這就是為什麼稱呼這東西(後面遇到容易混淆的地方,我們稱之為:類物件)也是類的原因。同時,它也是一個物件,因此你可以對它做如下操作:
賦值給變數
複製它
為它增加屬性(attribute)
作為引數傳值給函式
舉例:
>>> print(ObjectCreator) # 你可以列印一個類,因為它同時也是物件
<class '__main__.ObjectCreator'>
>>> def echo(o):
... print(o)
...
>>> echo(ObjectCreator) # 作為引數傳值給函式
<class '__main__.ObjectCreator'>
>>> print(hasattr(ObjectCreator, 'new_attribute'))
False
>> > ObjectCreator.new_attribute = 'foo' # you can add attributes to a class
>>> print(hasattr(ObjectCreator, 'new_attribute'))
True
>>> print(ObjectCreator.new_attribute)
foo
>>> ObjectCreatorMirror = ObjectCreator # 將類賦值給變數
>>> print(ObjectCreatorMirror.new_attribute)
foo
>>> print(ObjectCreatorMirror())
<__main__.ObjectCreator object at 0x8997b4c>
動態建立類
既然類也是物件,那麼我們就可以在執行的時候建立它,跟建立物件一樣自然。
首先,我們使用class
關鍵字定義一個產生類的函式:
>>> def choose_class(name):
... if name == 'foo':
... class Foo(object):
... pass
... return Foo # return the class, not an instance
... else:
... class Bar(object):
... pass
... return Bar
...
>>> MyClass = choose_class('foo')
>>> print(MyClass) # the function returns a class, not an instance
<class '__main__.Foo'>
>>> print(MyClass()) # you can create an object from this class
<__main__.Foo object at 0x89c6d4c>
這很容易理解吧。但是,這並不那麼動態啊。我們還是需要自己來寫這個類的程式碼。
既然類也是物件,那就應該有用來產生它的東西。這東西就是type。
先來說說你所認識的type。這個古老而好用的函式,可以讓我們知道一個物件的型別是什麼。
>>> print(type(1))
<type 'int'>
>>> print(type("1"))
<type 'str'>
>>> print(type(ObjectCreator))
<type 'type'>
>>> print(type(ObjectCreator()))
<class '__main__.ObjectCreator'>
實際上,type還有一個完全不同的功能,它可以在執行時產生類。type可以傳入一些引數,然後返回一個類。(好吧,必須承認,根據不同的傳入引數,一個相同的函式type
居然會有兩個完全不同的作用,這很愚蠢。不過python這樣做是為了保持向後相容性。)
下面舉例type建立類的用法。首先,對於類一般是這麼定義的:
>>> class MyShinyClass(object):
... pass
在下面,MyShinyClass也可以這樣子被創建出來,並且跟上面的建立方法有一樣的表現:
>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object
>>> print(MyShinyClass)
<class '__main__.MyShinyClass'>
>>> print(MyShinyClass()) # create an instance with the class
<__main__.MyShinyClass object at 0x8997cec>
type建立類需要傳入三個引數,分別為:
類的名字
一組"類的父類"的元組(tuple) (這個會實現繼承,也可以為空)
字典 (類的屬性名與值,key-value的形式,不傳相當於為空,如一般寫法中的pass).
下面來點複雜的,來更好的理解type傳入的三個引數:
class Foo(object):
bar = True
def echo_bar(self):
print(self.bar)
等價於:
def echo_bar(self):
print(self.bar)
Foo = type('Foo', (), {'bar':True, 'echo_bar': echo_bar})
想要看點有繼承關係的類的實現,來:
class FooChild(Foo):
pass
等價於:
FooChild = type('FooChild', (Foo, ), {})
回顧一下我們學到哪了: 在python中,類就是物件,並且你可以在執行的時候動態建立類.
那到底什麼是metaclass(元類)
metaclass 就是建立類的那傢伙。(事實上,type就是一個metaclass)
我們知道,我們定義了class就是為了能夠建立object的,沒錯吧?
我們也學習了,python中類也是物件。
那麼,metaclass就是用來創造“類物件”的類.它是“類物件”的“類”。
可以這樣子來理解:
MyClass = MetaClass()
MyObject = MyClass()
也可以用我們上面學到的type
來表示:
MyClass = type('MyClass', (), {})
說白了,函式type
就是一個特殊的metaclass.
python在背後使用type
創造了所有的類。type
是所有類的metaclass.
我們可以使用__class__
屬性來驗證這個說法.
在python中,一切皆為物件:整數、字串、函式、類.所有這些物件,都是通過類來創造的.
>>> age = 35
>>> age.__class__
<type 'int'>
>>> name = 'bob'
>>> name.__class__
<type 'str'>
>>> def foo(): pass
>>> foo.__class__
<type 'function'>
>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>
那麼,__class__
的__class__
又是什麼呢?
>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>
metaclass就是創造類物件的工具.如果你喜歡,你也可以稱之為"類的工廠".
type是python內建的metaclass。不過,你也可以編寫自己的metaclass.
__metaclass__
屬性
我們可以在一個類中加入__metaclass__
屬性.
class Foo(object):
__metaclass__ = something...
[...]
當你這麼做了,python就會使用metaclass來創造類:Foo。
注意啦,這裡有些技巧的。
當你寫下class Foo(object)
的時候,類物件Foo還沒有在記憶體中生成。
python會在類定義中尋找__metaclass__
。如果找到了,python就會使用這個__metaclass__
來創造類物件: Foo。如果沒找到,python就使用type來創造Foo。
請把下面的幾段話重複幾遍:
當你寫如下程式碼的時候:
class Foo(Bar):
pass
python做了以下事情:
Foo中有__metaclass__
這個屬性嗎?
如果有,python會在記憶體中通過__metaclass__
建立一個名字為Foo的類物件。
如果python沒有在Foo中找到__metaclass__
,它會繼續在Bar(父類)中尋找__metaclass__
,並嘗試做和前面同樣的操作。
如果python由下往上遍歷父類也都沒有找不到__metaclass__
,它就會在模組(module)中去尋找__metaclass__
,並嘗試做同樣的操作。
如果還是沒有找不到__metaclass__
, python才會用內建的type(這也是一個metaclass)來建立這個類物件。
現在問題來了,我們要怎麼用程式碼來實現__metaclass__
呢? 寫一些可以用來產生類(class)的東西就行。
那什麼可以產生類?無疑就是type
,或者type
的任何子類,或者任何使用到type
的東西都行.
自定義metaclass
使用metaclass的主要目的,是為了能夠在建立類的時候,自動地修改類。
一個很傻的需求,我們決定要將該模組中的所有類的屬性,改為大寫。
有幾種方法可以做到,這裡使用__metaclass__
來實現.
在模組的層次定義metaclass,模組中的所有類都會使用它來創造類。我們只需要告訴metaclass,將所有的屬性轉化為大寫。
# type也是一個類,我們可以繼承它.
class UpperAttrMetaclass(type):
# __new__ 是在__init__之前被呼叫的特殊方法
# __new__是用來建立物件並返回這個物件
# 而__init__只是將傳入的引數初始化給物件
# 實際中,你很少會用到__new__,除非你希望能夠控制物件的建立
# 在這裡,類是我們要建立的物件,我們希望能夠自定義它,所以我們改寫了__new__
# 如果你希望的話,你也可以在__init__中做些事情
# 還有一些高階的用法會涉及到改寫__call__,但這裡我們就先不這樣.
def __new__(upperattr_metaclass, future_class_name,
future_class_parents, future_class_attr):
uppercase_attr = {}
for name, val in future_class_attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
return type(future_class_name, future_class_parents, uppercase_attr)
這裡的方式其實不是OOP(面向物件程式設計).因為我們直接呼叫了type,而不是改寫父類的__type__
方法.
所以我們也可以這樣子處理:
class UpperAttrMetaclass(type):
def __new__(upperattr_metaclass, future_class_name,
future_class_parents, future_class_attr):
uppercase_attr = {}
for name, val in future_class_attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
return type.__new__(upperattr_metaclass, future_class_name,
future_class_parents, uppercase_attr)
這樣子看,我們只是複用了type.__new__
方法,這就是我們熟悉的基本的OOP程式設計,沒什麼魔法可言.
你可能注意到,__new__
方法相比於
type(future_class_name, future_class_parents, future_class_attr)
多了一個引數: upperattr_metaclass, 請別在意,這沒什麼特別的:__new__
總是將"它要定義的類"作為第一個引數。
這就好比是 self 在類的一般方法(method)中一樣,也是被作為第一個引數傳入。
當然啦,這裡的名字的確是我起的太長了。就像self一樣,所有的引數都有它們傳統的名稱。
因此,在實際的程式碼中,一個metaclass應該是寫成下面樣子的:
(我們同時使用常見的super來讓程式碼更清晰)
class UpperAttrMetaclass(type):
def __new__(cls, clsname, bases, attrs):
uppercase_attr = {}
for name, val in attrs.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, attrs)
使用了 metaclass 的程式碼是比較複雜,但我們使用它的原因並不是為了複雜, 而是因為我們通常會使用 metaclass 去做一些晦澀的事情,比如, 依賴於自省,控制繼承等等。
相關推薦
【原創】Python 對象創建過程中元類, __new__, __call__, __init__ 的處理
diff regular luci 自定義 weight ica 一般來說 att ray 原始type: type是最原始的元類,其__call__方法是在你使用" t_class = type(classname_string, base_classes_tuple,
5.1 編程語言的基元類型
屬性 long 測試 byte 發生 代碼 arr dsi 算數運算 編譯器直接支持的數據類型成為基元類型(primitive type)。基元類型直接映射到 Framework類庫(FCL)中存在的類型。 int a = 0; // Most convenient
C++之友元函數和友元類
res con 形參 display tle private 一點 second main 通過friend關鍵字,我們可以將不屬於當前類的一個函數在當前類中加以聲明,該函數便可以成為當前類的友元函數。#include<iostream>using namesp
友元函數友元類.
log mod http eache src con 類對象 之間 封裝 友元能夠理解為是類的“朋友”。它能夠訪問類的保護和私有成員。友元的作用在於提高程序的執行效率,可是,它破壞了類的封裝性和隱藏性。友元能夠是一個函數,該函數被稱為友元函數;友元也能夠是一個類
關於元類的一些使用心得
col 作業 數據 如果 屬性信息 一個 mod 魔法 分析 作廖老師的Python實戰作業時,對元類這個魔法方法有些小心得。 元類這個黑魔法和linux中root都是bug般的存在,不想糾纏概念性東西,試著從Python解釋器的角度分析下元類的執行過程。 其實,元類的主要
python 元類的簡單解釋
.html www ren 有用 copy tle 例子 sky -i 本文轉自博客:http://www.cnblogs.com/piperck/p/5840443.html 作者:piperck python 類和元類(metaclass)的理解和簡單運用 (一) p
廖雪峰Python學習筆記——使用元類
ram form 創建對象 字典 comm params int name 學習筆記 元類(MetaClasses) 元類提供了一個改變Python類行為的有效方式。 元類的定義是“一個類的類”。任何實例是它自己的類都是元類。 class demo(object):
python中的元類(metaclass)
優先 裝飾器 target {} pass get tac 搜索 items 認識python中元類的準備工作。 1,首先需要明白一個概念就是python中一切皆為對象。 input: class Trick(object): pass print type(‘1234
C# 基元類型
多人 sig float 說話 cal img tac com 編程 C#編程中,初始化一個整數有兩種方式: (1)、較繁瑣的方法,代碼如下: Int32 a = new Int32(); (2)、極簡的方法,代碼如下: int a=0; 對比兩種方法,分析如下:
Django-models class Meta:元類
設置 div rmi 應用 默認值 htm 字段排序 als 簡單 Django模型之Meta選項詳解 Model 元數據就是 "不是一個字段的任何數據" -- 比如排序選項, admin 選項等等. Django模型類的Meta是一個內部類,它用於定義一些Djan
python面向對象( item系列,__enter__ 和__exit__,__call__方法,元類)
屬性 eba callable 好處 繼承方式 類的創建 完成 __weak 依次 python面向對象進階(下) item系列 __slots__方法 __next__ 和 __iter__實現叠代器 析構函數 上下文管理協議 元類一
【Python】【元編程】【三】【元類】
無法使用 import iter 時也 food ini lin abc __init__ ‘‘‘# str、 type 和 LineItem 是object 的子類 str、 object 和 LineItem 是 type 的實例,因為它們都是類object 類和
【練習】友元類和友元函數
turn main spa std return col print pri circle #include <iostream> using namespace std; class Circle; class Point { private: fl
Python 元類
tac sorted this result init one ati handler ... http://ningning.today/2017/01/25/python/simple-python-metaclass/ https://stackoverflow.co
Python之Metaclass詳解,Python之元類
turned 除了 方法 寫法 找到 類對象 global 所在 code 本人Java程序員一枚,這幾天閑來無事就自學了下Python,學到Metaclass感覺有點迷惑,就在網上查相關資料,在棧溢出(stackoverflow)網站上看到一個關於metaclass的回答
元類相關(type & metaclass)
type 不同 ans -s family 1.2 的人 elf 創建 """metaclass作用: 1) 攔截類的創建 2) 修改類 3) 返回修改之後的類 """ """為什麽要用metaclass類而不
元類metaclass
add 手動 isp aps pos sel list 方式 base 閱讀目錄 一 知識儲備 二 引子(類也是對象) 三 什麽是元類? 四 創建類的兩種方式 五 自定義元類控制類的行為 六 練習題 一 知識儲備 exec:三個參數 參數一:字符串形式的
C++中的友元函數和友元類
pan string 拷貝構造函數 student 私有 oid 一個 each cor 友元函數可以修改類的私有屬性,寫在類的public/private/protected底下都可以。友元函數的函數體寫在類的外面時,寫法和普通函數一樣,不需要加friend關鍵字,但函數
python全棧開發基礎【補充】metaclass(元類)
認識 全棧 rgs bubuko class a alt 創建 繼承 圖片 一、創建類的執行流程 二、元類的認識 什麽是元類呢?在Python3中繼承type的就是元類 二、元類的示例 # 方式一 class MyType(type): ‘‘‘繼承type
元類type
body 運行 賦值 ini 展示 rgs tac bject meta 自定義元類來創建類 python中的類也是對象,是type類的對象 在編譯器運行到class Foo的時候自動運行Foo = type(‘Foo‘,{object,},{})來創建類對象並放入內存中