1. 程式人生 > >Python高階特性(3): Classes和Metaclasses

Python高階特性(3): Classes和Metaclasses

類和物件

類和函式一樣都是Python中的物件。當一個類定義完成之後,Python將建立一個“類物件”並將其賦值給一個同名變數。類是type型別的物件(是不是有點拗口?)。

類物件是可呼叫的(callable,實現了 __call__方法),並且呼叫它能夠建立類的物件。你可以將類當做其他物件那麼處理。例如,你能夠給它們的屬性賦值,你能夠將它們賦值給一個變數,你可以在任何可呼叫物件能夠用的地方使用它們,比如在一個map中。事實上當你在使用map(str, [1,2,3])的時候,是將一個整數型別的list轉換為字串型別的list,因為str是一個類。可以看看下面的程式碼:

12345678910111213141516171819202122232425 >>>classC(object):...def __init__(self,s):...prints...>>>myclass=C>>>type(C)<type'type'>>>>type(myclass
)<type'type'>>>>myclass(2)2<__main__.Cobjectat0x10e2bea50>>>>map(myclass,[1,2,3])123[<__main__.Cobjectat0x10e2be9d0>,<__main__.Cobjectat0x10e2bead0>,<__main__.Cobjectat0x10e2beb10>]&
gt;>>map(C,[1,2,3])123[<__main__.Cobjectat0x10e2be950>,<__main__.Cobjectat0x10e2beb50>,<__main__.Cobjectat0x10e2beb90>]>>>C.test_attribute=True>>>myclass.test_attributeTrue

正因如此,Python中的“class”關鍵字不像其他語言(例如C++)那樣必須出現在程式碼main scope中。在Python中,它能夠在一個函式中嵌套出現,舉個例子,我們能夠這樣在函式執行的過程中動態的建立類。看程式碼:

12345678910111213141516171819 >>>def make_class(class_name):...classC(object):...def print_class_name(self):...print class_name...C.__name__=class_name...returnC...>>>C1,C2=map(make_class,["C1","C2"])>>>c1,c2=C1(),C2()>>>c1.print_class_name()C1>>>c2.print_class_name()C2>>>type(c1)<class'__main__.C1'>>>>type(c2)<class'__main__.C2'>>>>c1.print_class_name.__closure__(<cell at0x10ab6dbe8:str objectat0x10ab71530>,)

請注意,在這裡通過make_class建立的兩個類是不同的物件,因此通過它們建立的物件就不屬於同一個型別。正如我們在裝飾器中做的那樣,我們在類被建立之後手動設定了類名。同樣也請注意所建立類的print_class_name方法在一個closure cell中捕捉到了類的closure和class_name。如果你對closure的概念還不是很清楚,那麼最好去看看前篇,複習一下closures和decorators相關的內容。

Metaclasses

如果類是能夠製造物件的物件,那製造類的物件又該叫做什麼呢(相信我,這並不是一個先有雞還是先有蛋的問題)?答案是元類(Metaclasses)。大部分常見的基礎元類都是type。當輸入一個引數時,type將簡單的返回輸入物件的型別,這就不涉及元類。然而當輸入三個引數時,type將扮演元類的角色,基於輸入引數建立一個類並返回。輸入引數相當簡單:類名,父類及其引數的字典。後面兩者可以為空,來看一個例子:

123456 >>>MyClass=type("MyClass",(object,),{"my_attribute":0})>>>type(MyClass)<type'type'>>>>o=MyClass()>>>o.my_attribute0

特別注意第二個引數是一個tuple(語法看起來很奇怪,以逗號結尾)。如果你需要在類中安排一個方法,那麼建立一個函式並且將其以屬性的方式傳遞作為第三個引數,像這樣:

123456789 >>>def myclass_init(self,my_attr):...self.my_attribute=my_attr...>>>MyClass=type("MyClass",(object,),{"my_attribute":0,"__init__":myclass_init})>>>o=MyClass("Test")>>>o.my_attribute'Test'>>>o.__init__<bound method MyClass.myclass_init of<__main__.MyClass objectat0x10ab72150>>

我們可以通過一個可呼叫物件(函式或是類)來自定義元類,這個物件需要三個輸入引數並返回一個物件。這樣一個元類在一個類上實現只要定義了它的__metaclass__屬性。第一個例子,讓我們做一些有趣的事情看看我們能夠用元類做些什麼:

12345678910 >>>def mymetaclass(name,parents,attributes):...return"Hello"...>>>classC(object):...__metaclass__=mymetaclass...>>>printCHello>>>type(C)<type'str'>

請注意以上的程式碼,C只是簡單地將一個變數引用指向了字串“Hello”。當然了,沒人會在實際中寫這樣的程式碼,這只是為了演示元類的用法而舉的一個簡單例子。接下來我們來做一些更有用的操作。在本系列的第二部分我們曾看到如何使用裝飾器類來記錄目標類每個方法的輸出,現在我們來做同樣的事情,不過這一次我們使用元類。我們借用之前的裝飾器定義:

1234567891011121314151617181920212223 def log_everything_metaclass(class_name,parents,attributes):print"Creating class",class_namemyattributes={}forname,attr inattributes.items():myattributes[name]=attrifhasattr(attr,'__call__'):myattributes[name]=logged("%b%d%Y-%H:%M:%S",class_name+".")(attr)returntype(class_name,parents,myattributes)classC(object):__metaclass__=log_everything_metaclassdef __init__(self,x):self.x=xdef print_x(self):print self.x# Usage:print"Starting objectcreation"c=C("Test")c.print_x()
12345678 # Output:Creating classCStarting objectcreation-Running'C.__init__' on Aug 05 2013 - 13:50:58-Finished'C.__init__', execution time = 0.000s-Running'C.print_x' on Aug 05 2013 - 13:50:58Test-Finished'C.print_x', execution time = 0.000s

如你所見,類裝飾器與元類有著很多共同點。事實上,任何能夠用類裝飾器完成的功能都能夠用元類來實現。類裝飾器有著很簡單的語法結構易於閱讀,所以提倡使用。但就元類而言,它能夠做的更多,因為它在類被建立之前就運行了,而類裝飾器則是在類建立之後才執行的。記住這點,讓我們來同時執行一下兩者,請注意執行的先後順序:

相關推薦

Python高階特性3: ClassesMetaclasses

類和物件 類和函式一樣都是Python中的物件。當一個類定義完成之後,Python將建立一個“類物件”並將其賦值給一個同名變數。類是type型別的物件(是不是有點拗口?)。 類物件是可呼叫的(callable,實現了 __call__方法),並且呼叫它能夠建立類

Python高階特性2:Closures、Decoratorsfunctools

裝飾器(Decorators) 裝飾器是這樣一種設計模式:如果一個類希望新增其他類的一些功能,而不希望通過繼承或是直接修改原始碼實現,那麼可以使用裝飾器模式。簡單來說Python中的裝飾器就是指某些函式或其他可呼叫物件,以函式或類作為可選輸入引數,然後返回函式或類

Python高階特性1:Iterators、Generatorsitertools

【譯註】:作為一門動態指令碼語言,Python對程式設計初學者而言很友好,豐富的第三方庫能夠給使用者帶來很大的便利。而Python同時也能夠提供一些高階的特性方便使用者使用更為複雜的資料結構。本系列文章共有三篇,本文是系列的第一篇,將會介紹迭代器、生成器以及iter

C# 高階特性Attribute反射

使用Attribute的時候大多需要用到反射,所以放在一起。 Attribute: 我的理解是,它可以給你的類、方法、欄位等新增一些描述性語言,在執行期間又可以通過反射的方法獲取它的內容。 在編譯期間就初始化好了。 反射: 通過反射可以在不瞭解物件的內容時,操作物件。 優點

python高階特性1:切片

原文 取一個list或tuple的部分元素是非常常見的操作。比如,一個list如下: L = [‘Michael’, ‘Sarah’, ‘Tracy’, ‘Bob’, ‘Jack’] 取前3個元素,應該怎麼做?

程式設計與演算法第十週 c++新特性c++高階主題3

強制型別轉換 :static_cast、interpret_cast、const_cast、dynamic_cast 1、static_cast:static_cast 用來進行比較“自然”和低風險的轉

Python高階特性切片 迭代 列表生成式 生成器 迭代器學習筆記

在Python中,程式碼不是越多越好,而是越少越好。程式碼不是越複雜越好,而是越簡單越好。 基於這一思想,Python中有非常有用的高階特性,1行程式碼能實現的功能,決不寫5行程式碼。請始終牢記,程式碼越少,開發效率越高。 切片 (Slice) 切

python連線資料庫3插入更新刪除

首先說一下事務與回滾: 事務就是執行單元的集合 回滾就是回到執行之前的狀態 事務應該具有4個屬性:原子性、一致性、隔離性、永續性。這四個屬性通常稱為ACID特性。① 原子性(atomicity)。一個

python學習筆記7-高階特性-列表生成式與生成器

 [d for d in os.listdir('.')] ['.bash_logout', '.bash_profile', '.bashrc', '.cshrc', '.tcshrc', 'anaconda-ks.cfg', '.cache', '.config', 'perl5', '.bash_hi

python爬蟲系列3:使用SeleniumBeautifulSoup獲取12306一個月內所有車次車票情況

首先針對標題說明一下,本次的獲取資料是指定出發地和目的地之間的車次,不是整個網站所有車次。 在此操作之前,請確保自己的相關的庫都已經安裝完全,這裡可沒有教安裝庫的方法哦~~~~好的,往下走,這次的目標網頁是 https://kyfw.12306.cn/otn/leftTic

Python學習筆記3for循環while循環

循環語句 con while循環 art start 開始 count als 一輪 2019-02-25 (1)break語句:終止當前循環,跳出循環體。 (2)continue語句:終止本輪循環並開始下一輪循環(在下一輪循環開始前,會先測試循環條件)。 (3)fo

Python進階3_進程與線程中的lock互斥鎖、遞歸鎖、信號量

fun 我們 bsp 控制 支持 發生 class 線程 數據操作 1、同步鎖 (Lock) 當各個線程需要訪問一個公共資源時,會出現數據紊亂 例如: 1 import threading,time 2 def sub(): 3 global num

python裝飾器3

urn python裝飾器 裝飾 int 裝飾器 func 實現 ret test 另一種實現方式: 1 __author__ = "csy" 2 3 def test2(func): 4 def test1(): 5 func()

Python爬蟲學習3

collect nbsp pri div time urlparse links ews 是否 在慕課網學習並創建了一個簡單的爬蟲包,爬取百度百科相關詞條信息 程序中會用到第三方解析包(BeautifulSoup4),Windows環境下安裝命令:pip install B

自興人工智能------------python入門基礎2列表元祖

div 最小值 最大值 布爾 str 列操作 一段 ext .so 一.通用序列操作: 列表中所有序列都可以進行特定的操作,包括索引(indexing).分片(slicing).序列相加(adding).乘法,成員資格,長度,最小值,最大值,下面會一一介紹這些操作法。 1.

sh變量特性3默認特性

sh默認變量變量說明$0當前腳本的文件名$n傳遞給腳本或函數的參數,n是數字,第n個參數$#傳遞給腳本或函數的參數個數$*傳遞給腳本或函數的所有參數$@傳遞給腳本或函數的所有參數。被””包含時,與$*稍有不同$?上個命令的退出狀態,或函數返回值$$當前shell的進程ID示例:#!/bin/bashecho

Python實用筆記 3條件判斷

縮進 elif 改進 class 實用 tee 原因 print string 可以執行多條語句,靠的是縮進原則,看起來也更板紮(註意冒號) age = 3 if age >= 18: print(‘adult‘) elif age >= 6:

Python學習筆記3

python重要的數據類型Dict和Setdict通過key 查找value(key和value關聯)花括號{ }表示這是一個dict,然後按照key:value,寫出來即可。最後一個key:value的都好可以省略 註意: 單元素的tuple必須在後面多家加一個逗號dict最後的逗號可以省略由於dict也是

Python開發環境3:使用Eclipse+PyDev插件創建Django項目

ffffff postgresq 項目上線 右鍵 ont pat iat ngs ora OS:Windows 10家庭中文版,Python:3.6,Eclipse:Oxygen.1a Release (4.7.1a), PyDev:6.3.2,Django:2.0.3

Python入門筆記——2列表元組

typeerror 和集 true perl 參數 集合 list函數 方法的參數 運算 一、序列 python包含6種內建的序列:列表、元組、字符串、Unicode字符串、buffer對象和xrange對象。序列中每個元素被分配一個序號即索引,第一個索引為0,