1. 程式人生 > >Python技巧:元類(Metaclasses)和利用Type構建的動態類(Dynamic Classes)

Python技巧:元類(Metaclasses)和利用Type構建的動態類(Dynamic Classes)

`metaclass`和`type`關鍵字在Python程式碼中較少被使用(也正因如此,它們的作用也沒有很好的被理解)。在這篇文章中,我們將探究`type()`的型別(types)和跟`metaclasses`相關的`type`的用法。

這是我的型別麼?
首先來看`type()`的第一個廣為人知的用法,即檢視一個物件的型別。Python初學者常常會對此有這樣的疑問:“我認為Python中是沒有型別的啊!”可事實正相反,Python中的一切都有型別(即使是型別關鍵字‘types’本身!),這是因為Python中的一切都是物件(object)。下面來看幾個例子:

>>> type(1)
<class 'int'>
>>> type('foo')
<class 'str'>
>>> type(3.0)
<class 'float'>
>>> type(float)
<class 'type'>

`type`的型別

一切看起來都是那麼理所當然,直到我們檢視float的型別,`<class 'type'>`?這是怎麼回事?繼續往下看:

>>> class Foo(object):
...     pass
...
>>> type(Foo)
<class 'type'>

啊,我們又看到了`<class 'type'>`。很顯然,所有類自身的型別都是`type`(無論是內建型別或是使用者定義型別的類)。那麼`type`的`type`又是什麼呢?
>>> type(type)
<class 'type'>

好吧,測試到此為止。`type`是所有型別的型別,包括它自身。事實上,`type`是一個元類`metaclass`,也就是“一個用來構建類的東西”。諸如`list()`這樣的類,是用來構建類的例項(instances),例如`my_list=list()`。而元類則以同樣的方式來構建型別,例如以下程式碼:
class Foo(object):
    pass

建立自己的元類

與常規類類似,元類也可以由使用者自定義。具體用法是將目標類的`__metaclass__`屬性設定為自定義的`metaclass`。`metaclass`必須是可呼叫(callable)的,並且返回一個`type`。通常,使用者可以使用一個函式來設定`__metaclass__`屬性,這個函式是`type`的另一種用法:利用三個引數建立一個新類。

`type`的另一面

如上文所述,`type`有著另一種完全不同的用法。這種用法由傳入三個引數:type(name, bases, dict)能夠建立一個新的型別。以下是建立一個新類的通常做法:

class Foo(object):
    pass

而我們能夠通過以下的程式碼達到同樣的效果:
Foo = type('Foo', (), {})

目前Foo是一個名為“Foo”的類的引用,這個名為“Foo”的類以object為基類(通過type建立的類,如果沒有特別指定其基類,將會預設建立新型別的類)。


這麼做看起來非常棒,但如果我們需要向Foo中新增成員函式該怎麼辦呢?這很容易通過設定Foo的類屬性辦到:
def always_false(self):
    return False

Foo.always_false = always_false

上面的程式碼也能夠寫成如下形式:
Foo = type('Foo', (), {'always_false': always_false})

bases引數是Foo的基類列表,在上例中我們留空了,同時可以用type從Foo類建立一個新類:
FooBar = type('FooBar', (Foo), {})

這麼做有什麼用?

介紹完`type`和`metaclasses`之後問題隨之而來:“我該什麼時候使用它們呢?”答案是顯然的,在日常的工作中我們並不一定會經常使用到它們。但當我們想要動態地建立類時,利用type是一個很合適的解決方案。讓我們來看個例子:

sandman是我寫的一個庫,這個庫能夠自動地為已有資料庫生成REST API以及基於網頁的管理員介面(不需要配置模板程式碼)。大部分工作由SQLAlchemy這樣一個物件關係對映框架完成。

利用SQLAlchemy註冊一個數據庫表只有一種方法:建立一個描述這個資料表的模板類(跟Django中得models不同)。為了SQLAlchemy能夠識別一個表,一個對應的類必須通過某種方式生成。因為sandman沒有關於資料庫結構的任何先驗知識,所以不能通過提前準備模板類的方式來註冊資料表。相反,需要通過探查資料庫動態的生成模板類。是不是聽起來很熟悉?無論何時當你想動態地建立一個新類,type都將是你唯一正確地選擇。

以下是sandman中的相關實現程式碼:

if not current_app.endpoint_classes:
    db.metadata.reflect(bind=db.engine)
    for name in db.metadata.tables():
        cls = type(str(name), (sandman_model, db.Model),
                {'__tablename__': name})
        register(cls)

正如你所見,如果使用者沒有手動的為一個表建立模板類,程式碼將動態的建立,並且利用表名設定`__tablename__`屬性(SQLAlchemy將利用這個屬性匹配表與相應的模板類)。

總結

在這篇文章中,我們討論了`type`、`metaclasses`的兩種用法和使用時機。儘管`metaclasses`是一個多多少少會讓人感到困惑的概念,希望讀者現在對此能有一個清晰的認識,並在未來的學習中能夠使用。

-------------------------------------------

P.S. 吐個槽,csdn部落格為什麼還不支援Markdown?

相關推薦

Python技巧Metaclasses利用Type構建動態Dynamic Classes

`metaclass`和`type`關鍵字在Python程式碼中較少被使用(也正因如此,它們的作用也沒有很好的被理解)。在這篇文章中,我們將探究`type()`的型別(types)和跟`metaclasses`相關的`type`的用法。 這是我的型別麼?首先來看`ty

python基礎tuple列表list介紹

一,元組 1.元組的建立(可以把元組看作一個容器,任何資料型別都可以放在裡面)通過賦值方法建立元組In [5]: t = ("hello",2.3,2,True,{1:"hello",2:"world"},) In [6]: type(t)Out[6]: tuple In [7]: t = (1) I

python(七)與抽象基

imp 匿名 exec int 上下文 增加 abstract 分割 als 一、實例創建   在創建實例時,調用__new__方法和__init__方法,這兩個方法在沒有定義時,是自動調用了object來實現的。python3默認創建的類是繼承了object。 c

python基礎組、字典、深淺拷貝與函數

dictionary python tuple 函數 開發 小生博客:http://xsboke.blog.51cto.com 小生 Q Q:1770058260 -------謝謝您的參考,如有疑問,歡迎交流一、 元

重建二叉樹依據先序遍歷或者後序遍歷中序遍歷重建二叉樹

off 相同 tree int roo 節點 先序 throw -a 對於一顆二叉樹。能夠依據先序遍歷(或者後序遍歷)和中序遍歷(樹中不含反復的數字)又一次還原出二叉樹。 解析: 1. 先序遍歷序列的第一個元素必然是根節點,能夠由此獲取二叉樹的根節點。 2. 依

python基礎組的使用

元組 遍歷 方法元組的定義。使用( )來定義。null_tuple = () #定義一個空元組one_element = (‘one‘, ) #定義一個元素的元組,註意:只有一個元素後面必須帶逗號元組的修改、增加和刪除元素。元組是不可以修改,增加和刪除元素的!。但,

監督學習隨機梯度下降算法sgd批梯度下降算法bgd

這就是 影響 個數 執行 類型 http 關系 col pla 線性回歸 首先要明白什麽是回歸。回歸的目的是通過幾個已知數據來預測另一個數值型數據的目標值。 假設特征和結果滿足線性關系,即滿足一個計算公式h(x),這個公式的自變量就是已知的數據x,

抽象數據ADT面向對象編程OOP3.1數據型檢查

字符串 9.png lac per 不能被繼承 不變 play 困難 及其 數據類型在編程語言中: 類型是一組值以及可以對這些值進行操作 變量 存儲一個特定類型值的命名位置 基本數據類型: int 限制在±2 ^ 31的範圍內,或者大約為±20億

抽象數據ADT面向對象編程OOP3.2規約

閱讀 合同 表示 自己實現 api 運行 技術分享 AR result API:application programming interface,是Java自己提供的標準類庫; 查API就是讓你去看Java自己實現的函數,查看它怎樣調用,要傳什麽參數等

K均值聚K-means高斯混合聚Mixture of Gaussian Models

math del 一個 ans line k-均值聚類 初始化 gaussian 樣本 K-means算法流程 給定條件: ????example set: \((x_1, y_1), (x_2, y_2), \dots, (x_N, y_N)\) 初始化: ????K個簇

MyBatis自動生成實體、DAO介面Mapping對映檔案的程式碼逆向工程

MyBatis屬於一種半自動的ORM框架,它需要程式設計師自己編寫sql語句和對映檔案,但是編寫對映檔案和sql語句很容易出錯,所以mybatis官方提供了Generator生成器,自動生成DAO介面。實體類和Mapping。這個生成器是根據單表自動生成myba

二叉樹後序,遞迴非遞迴,應用求祖先問題

1  宣告 2 後序 a 遞迴 void PostOrder(BiTree T) { if (T) { PostOrder(T->lChild); P

磁碟分割槽形式主啟動記錄MBR全域性唯一標識分割槽表GPT

       過年放假回家到朋友家玩耍,在與朋友交談的時候提到了計算機,於是朋友便讓我幫他重新安裝系統。他電腦的系統我清楚,是WIN8的,之前就是我幫他安裝了,不過用著WIN8一直不是很順手,因此讓我

Exchange企業實戰技巧郵件中使用數字簽名郵件加密功能

clip 節點 模式 chan 打開 ont 發送 菜單欄 電子郵件 SMTP最初是為了在封閉的網絡中傳送相對來說不太重要的簡短郵件,因此SMTP傳輸郵件時,安全性不高。自從安全、多用途INTERNET郵件擴展(S/MIME)成為增強SMTP電子郵件安全功能的標準,使得實現

跨域問題相關知識詳解原生jsjquery兩種方法實現jsonp跨域

syn con 加載 developer 兩種方法 ray exe 編寫 分組 1、同源策略 同源策略(Same origin policy),它是由Netscape提出的一個著名的安全策略。同源策略是一種約定,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,則瀏覽

LeetCode 381. Insert Delete GetRandom O(1) - Duplicates allowed 插入刪除獲得隨機數 常數時間 允許重復項

anti mean 插入 another right operation view 回顧 true Design a data structure that supports all following operations in average O(1) time. N

python入門1-99所有數的的等式

print spa color python入門 空白 env temp 字符串 如果 1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #1-99所有數的和的等式 4 #start(開始,譯音:思達二測)sum

內核級線程KLT用戶級線程ULT

版權 href 說明 撤銷 設計 不能 開發 系統 恢復 內核級線程(KLT)和用戶級線程(ULT) tags: KLT ULT 內核級線程 用戶級線程 引言:本文涉及到操作系統的內核模式和用戶模式,如果不太懂的話,可以參看我的這篇文章內核模式和用戶模式,其中簡單的進行

用ASP.NET Core MVC EF Core 構建Web應用

work nal nta 多個 包括 catch web 應用 自動 選項卡 本節學習如何執行基本的 CRUD (創建、 讀取、 更新、 刪除) 操作。 自定義“詳細信息”頁 學生索引頁的基架代碼省略了 Enrollments 屬性,因為該屬性包含一個集合。 在“詳細信息”

用ASP.NET Core MVC EF Core 構建Web應用

fix pro school time lap namespace 繼承映射 數據庫表 eas 在上一節中,已經處理了並發異常。 本節將演示如何在數據模型中實現繼承。 在面向對象的編程中,可以使用繼承以便於重用代碼。 在本教程中,將更改 Instructor和 Studen