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)和面向對象編程(OOP)3.1數據類型和類型檢查
字符串 9.png lac per 不能被繼承 不變 play 困難 及其 數據類型在編程語言中: 類型是一組值以及可以對這些值進行操作 變量 存儲一個特定類型值的命名位置 基本數據類型: int 限制在±2 ^ 31的範圍內,或者大約為±20億
抽象數據類型(ADT)和面向對象編程(OOP)3.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電子郵件安全功能的標準,使得實現
跨域問題相關知識詳解(原生js和jquery兩種方法實現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