面向對象概述:
python支持兩種編程方式,函數式編程、面向對象編程,三大特性:封裝、繼承、多態。
封裝:把功能相同的方法封裝到類中、數據封裝到對象中;
繼承:如果多個類中有相同的方法和數據,避免重複編寫,把相同的方法提取出來放在基類中,給子類進行繼承使用;
多態:python天生支持多態,對於參數可以傳入任何類型的對象,只要保證有所要的send方法即可。
面向對象進階:
__init__,初始化對象
__new__,創建對象
__call__,對象()
__getattr__,對象.屬性
__setattr__,對象.屬性 = 'Jsom'
__delattr__,刪除屬性
__getitem__,對象['']
__setitem__,對象[''] = 'Jsom'
__delitem__,刪除屬性
__mro__,查找成員順序,繼承關係
__str__,
__repr__,
__iter__,
__enter__,with 運行前調用
__exit__,with 運行結束調用
__dict__,
__class__,
__add__,對象+一個對象
metaclass:類的創建方式與自定義創建類的方式
類創建:
class Foo():
pass
Foo = type('Foo',(Object,),{}) # 類的創建默認由type()完成的
如何指定類由自定義的type創建
class Mytype(object): # 自定義創建類方法
pass
class Foo(object,metaclass=Mytype): # 查找自定義Mytype
# __metaclass__=Mytype python2.x
pass
Foo = Mytype('Foo',(object,),{})
默認執行順序:( __init__ --> __call__ --> __new__ --> __init__)。
PS:在類的繼承關係中,基類指定了metaclass的話,子類也會根據基類指定的metaclass創建。
1、引言
(1)類和實例:類是對象的定義,實例是“真正的實物”。
定義類:類名通常大寫字母打頭。
class MyNewObjectType(bases):
'define MyNewObjectType class'
class_suite
bases可以是一個(單繼承)或多個(多重繼承)用於繼承的父類。
object是“所有類之母”。
Python調用類進行實例化,實例化類不使用new關鍵字。
c=MyNewObjectType()
類有時可以僅作爲名稱空間。
class MyData(object):
pass
mathobj=MyData()
mathobj.x=4
mathobj.y=5
這些屬性是動態的,不需要在構造器中或其他任何地方爲它們預先聲明或賦值。
(2)方法
定義方法:屬性和方法使用駝峯記法。
class MyDataWithMethod(object):
def printFoo(self):
print ....
所有方法都存在self,表示實例對象本身。
靜態方法或類方法不需要self。
__init__()方法類似於構造函數,但不同於構造函數,因Python不new。該方法在實例被創建後,實例化調用返回這個實例之前被調用。
(3)棧
.push() 添加
.pop() 獲取並刪除
.top() 獲取
# 棧的管理方式
class Stack(object):
def __init__(self):
self.data = []
def push(self,val):
self.data.append(val)
def pop(self):
return self.data.pop()
def top(self):
return self.data[-1]
_stack = Stack()
_stack.push('小明')
_stack.push('小強')
print(_stack.pop()) # pop取出數據並刪除相對應的值
print(_stack.pop())
print(_stack.top()) # top取出數據不刪除 輸出報錯 因爲 pop 已經刪除了倒數第一個內容
2、面向對象編程常用術語
抽象/實現:建模 現實化
封裝/接口
合成:聯合、聚合
繼承/派生
泛化/特化
多態
自省/反射
3、類
Python中,一切皆爲對象,下面是類的定義語法。
class ClassName(object):
'class documentation string'
class_suite
Python不支持純虛函數(像C++)或者抽象方法等,替代方案:在基類方法中引發NotImplementedError異常。
3.1、執行父類的方法
class Base(object):
def func(self):
print('Base.func')
class Foo(Base):
def func(self):
#方法一:根據__mro__的循序執行類中的方法
#super(Foo,self).func()
#方法二:主動執行Base類的方法
Base.func(self) # 類運行其中的方法必須傳入self,對象運行方法自動會識別。
print('Foo.func')
obj = Foo()
obj.Func()
'''
輸出:
Base.func
Foo.func
'''
3.2、暴露類中的方法給外部訪問
__slots__ = (暴露的方法屬性名....)
#__slots__ = (暴露的方法屬性名....)
class Foo(object):
__slots__ = ('name') # 只允許name屬性被外部訪問
def __init__(self):
self.name = '小明'
# self.age = 30
obj = Foo()
print(obj.name)
# print(obj.age)
'''
輸出:小明
'''
4、類屬性
(1)屬性
屬性(數據或函數),使用‘.’屬性標識符來訪問。
屬性本身也是一個對象,也有自己的屬性,所以訪問屬性時會形成一個屬性鏈。
(2)類屬性/實例數據屬性
實例屬性在OOP中用得最多,類屬性僅當需要有更加“靜態”數據類型時才變得有用,它和任何實例無關,方法是類屬性。
Python要求,沒有實例,方法不能被調用。方法必須“綁定”到一個實例才能直接被調用。非綁定的方法可能可以被調用,但實例對象一定要明確給出,才能保證調用成功。然而,不管是否綁定,方法都是它所在的類的固有屬性,即使它們幾乎總是通過實例來調用的。
(3)確定一個類有哪些屬性的方法
使用內建函數dir()
訪問類的字典屬性__dict__
內建函數vars()接受類對象作爲參數,返回類的__dict__屬性的內容。
(4)特殊的類屬性
C.__name__ 類C的名字(字符串)
C.__doc__ 類C的文檔字符串
C.__bases__ 類C的所有父類構成的元組
C.__dict__ 類C的屬性
C.__module__ 類C所在的模塊(1.5)
C.__class__ 實例C對應的類(新式類)
5、實例
Python 2.2中統一了類和類型。
Python通過調用類對象來創建實例。
①__init__()方法
當類被調用,創建實例對象,對象創建後,調用__init__()方法完成特別的操作,執行完返回類對象,實例化結束。
Python沒有使用new創建實例,沒有定義構造器,由Python創建對象。
②__new__()方法
“構造器”方法,與__init__()方法相比,__new__()更像一個真正的構造器,因爲__new__()必須返回一個合法的實例,該實例作爲self傳給__init__()方法,__new__()方法會調用父類的__new__()方法來創建對象,在對內建類型進行派生時,__new__()方法可以實例化不可變對象。
③__del__()方法
“解構器”方法,當實例對象所有的引用都被清除掉後才執行該方法,用於實例釋放前進行特殊處理,__del__()方法只能被調用一次,使用__del__()方法,不要忘記首先調用父類的__del__()方法,del x不表示調用x.__del__()方法,僅引用計數減少,若存在循環引用,則對象的__del__()方法可能永遠不會被執行,__del__()方法未捕獲的異常會被忽略掉,除非有必要,否則不去實現__del__()方法,如果定義了__del__()方法,且實例是某個循環的一部分,垃圾回收器將不會終止這個循環,你需要自己顯式調用del。
6、實例屬性
方法嚴格來說是類屬性。實例僅擁有數據屬性。
(1)”實例化”實例屬性
①在__init__()方法中設置實例屬性。
設置實例的屬性可以在實例創建後任意時間進行。__init__()方法是設置這些屬性的關鍵點之一。Python能夠在“運行時”創建實例屬性(Python優秀特性之一)
②默認參數提供默認的實例安裝。
class HotelRoomClac(object):
def __init__(self,rt,sales=0.085,rm=0.1):
self.salesTax=sales
self.roomTax=rm
self.roomRate=rt
③__init__()方法應該返回None
__init__()方法不應該返回任何對象,因爲實例對象是自動在實例化調用後返回的。
(2)查看實例屬性
查看實例屬性:dir()、__dict__屬性、vars()
特殊的實例屬性:I.__class__、I.__dict__
內建類型屬性:內建類型可以使用dir()方法,不可以訪問__dict__特殊屬性,因爲在內建類型中,不存在這個屬性。
(3)類屬性和實例屬性(類似於自動變量和靜態變量)
可以採用類來訪問類屬性,若實例沒有同名的屬性的話,也可以用實例來訪問。
類屬性可以通過類或實例來訪問,不過只能使用類訪問類屬性時,才能更新類屬性的值。若在實例中更新類屬性,將會創建同名的實例屬性,“遮蔽”了類屬性。當刪除同名的實例屬性,類屬性才起作用。所以,從實例中訪問類屬性須謹慎。
class C(object): #定義類
version = 1.2#靜態成員
c=C()
C.version #通過類來訪問
c.version #通過實例來訪問
C.version+=0.1 #通過類(只能這樣)來更新類屬性
c.version =1.3 #任何對實例屬性的賦值都會創建一個實例屬性,而不是更新類屬性
當類屬性是可變類型時,並不會創建實例屬性,直接操作的是類屬性。
class Foo(object):
x={2003:'poe2'}
foo=Foo()
foo.x[2004]='valid path'
print(foo.x)
# 輸出:{2003:'poe2',2004:'valid path'}
print(Foo.x)
# 輸出:{2003:'poe2',2004:'valid path'} #生效了
del foo.x #刪除會報錯,因爲沒有遮蔽所以不能刪除掉
(4)類屬性持久性
類屬性,任憑整個實例(及其屬性)的如何進展,他都不理不睬(因此獨立於實例),類屬性的修改會影響到所有的實例。類屬性是靜態成員。
7、綁定和方法調用
(1)綁定
方法僅僅是類內部定義的函數,意味着方法是類屬性而不是實例屬性。
方法只有在類擁有實例時,才能被調用。方法被認爲是綁定到實例。方法中的變量self表示調用此方法的實例對象。
(2)方法調用
①調用非綁定的方法(不常見):類還未實例化。
class EmplAddrBookEntry(AddrBookEntry):
'Employee Address Book Entry class'
def __init__(self,nm,ph,em):
AddrBookEntry.__init__(self,nm,ph) #覆蓋父類方法
self.empid=em
②調用綁定方法:類已經實例化。
mc=MyClass()
mc.foo()
總結:方法定義於類內部,是類方法;方法綁定到實例,由實例調用;未綁定,由類調用。
8、靜態方法和類方法(2.2)
(1)經典類中創建靜態方法和類方法的例子
class TestStaticMethod:
def foo():
print 'calling static method foo()'
foo=staticmethod(foo) #內建函數,將方法轉換成靜態方法
class TestClassMethod:
def foo(cls): #cls爲類對象,類似於self
print 'calling class method foo()'
foo=classmethod(foo) #內建函數,將方法轉換成類方法
可以通過類或者實例調用這些函數。
tsm=TestStaticMethod()
TestStaticMethod.foo()
tsm.foo()
tcm=TestClassMethod()
TestClassMethod.foo()
tcm.foo()
(2)使用函數修飾符創建靜態方法和類方法的例子(2.4)
class TestStaticMethod:
@staticmethod
def foo():
print 'calling static method foo()'
class TestClassMethod:
@classmethod
def foo(cls):
print 'calling class method foo()'
9、繼承
(1)通過繼承覆蓋方法
子類定義與基類相同的方法時,會覆蓋(override)基類方法。
子類可以使用調用非綁定的基類方法的方法調用基類方法。
也可以使用super()內建方法調用基類方法。
當從一個帶構造器__init__()的類派生,如果你不去覆蓋__init__(),它將會被繼承並自動調用,但如果你在子類中覆蓋了__init__(),子類被實例化時,基類的__init__()就不會被自動調用。若要調用父類的__init__()方法,需要使用super()。
(2)從標準類型派生
經典類中,不能對標準類型進行子類化。
2.2後,可以對標準類型進行子類化。
子類化Python類型:其中一個是可變類型;另一個是不可變類型。
①子類化不可變類型
class RoundFloat(float):
def __new__(cls,val):
return float.__new__(cls,round(val,2))
所有的__new__()方法都是類方法,所以顯式地傳入類作爲第一個參數。
class RoundFloat(float):
def __new__(cls,val):
return super(RoundFloat,cls).__new__(cls,round(val,2))
通常使用super()內建函數去捕獲對應的父類以調用它的__new__()方法。
②子類化可變類型
class SortedKeyDict(dict):
def keys(self):
return sorted(super(SortedKeyDict,self).keys())
(3)多重繼承中方法解釋順序(MRO)
2.2之前,算法簡單:深度優先,從左至右進行搜索,取得在子類中使用的屬性。多重繼承取找到的第一個名字。
2.2提出新的MRO,算法思想是根據每個祖先類的繼承結構編譯出一張列表,包括搜索到的類,按策略刪除重複的。
2.3使用新的C3算法替換,採用廣度優先。
新式類有__mro__屬性,告訴你查找順序。
新式類使用經典類的MRO會失敗。
菱形效應
使用經典類的MRO,當實例化D時,不再得到C.__init__()之結果,而得到object.__init__()之結果。使用新式類,需要出現基類,這樣在繼承結構中,就形成了一個菱形。
補充:文檔字符串不會從基類中繼承過來。因爲文檔字符串對類,函數/方法,還有模塊來說都是唯一的。
10、類、實例、其他對象的內建函數
(1)issubclass()
布爾函數,判斷一個類是另一個類的子類或子孫類(一個類可視爲其自身的子類)。
issubclass(sub,sup)
從2.3開始,第二個參數可以是可能的父類組成的元組。只要sub是其中任何一個的子類都返回True。
(2)isinstance()
布爾函數,判定一個對象是否是另一個給定類的實例。isinstance(obj1,obj2)
obj1是obj2的一個實例,或是obj2的子類的一個實例時,返回True。 從2.2開始,obj2可以是一個元組,obj1是obj2元組中任何一個候選類型或類的實例時,就返回True。
(3)hasattr(),getattr(),setattr(),delattr()
*attr()系列函數可工作於各種對象,不限於類和實例。
*attr(obj,’attr’….)相當於操作obj.attr。
hasattr()布爾函數,決定一個對象是否有一個特定的屬性。
getattr(),setattr()相應地取得和賦值給對象的屬性。getattr()會在你試圖讀取一個不存在的屬性時,引發AttributeError異常。
delattr()刪除屬性。
(4)dir()
可用於實例或者類或者模塊,用於實例,顯示實例變量,還有在實例所在的類及所有它的基類中定義的方法和類屬性。用於類,顯示類及它所有基類的__dict__中的內容,但不會顯示定義在元類中的類屬性,用於模塊,顯示模塊的__dict__的內容。dir()不帶參數時,顯示調用者的局部變量。
(5)super()
super(MyClass,self)不是查找父類,而是根據__mro__的循序開始查找(繼承的先後進行查找);
obj是一個實例,isinstance(obj,type)必須返回True;
obj是一個類或類型,issubclass(obj,type)必須返回True;
super(MyClass,self).__init__()。
# super的執行查找循序
class Base(object):
def func(self):
super(Base,self).func()
print('Base.func')
class Bar(object):
def func(self):
print('Bar.func')
class Foo(Base,Bar):
pass
# 示例一
obj = Foo()
obj.func()
print(Foo.__mro__) # Foo.__mro__ 獲取類的繼承關係
'''
輸出:
(<class '__main__.Foo'>, <class '__main__.Base'>, <class '__main__.Bar'>, <class 'object'>)
'''
# 示例二
obj = Base()
obj.func()
'''
輸出:報出錯誤信息,因爲super對象中沒有 func 屬性
'''
(6)vars()
與dir()相似,只是給定的對象參數都必須有一個__dict__屬性。如果提供的對象沒有一個這樣的屬性,則會引發一個TypeError異常。
vars()返回一個字典,包含存儲於對象__dict__中的屬性(鍵)和值。如果沒有爲vars()提供參數,將顯示一個包含本地名稱空間的屬性(鍵)及其值的字典,也就是locals()。
11、用特殊方法定製類
Python特殊方法可以用來擴充類的功能,可以實現:模擬標準類型;重載操作符。
(1)Python中用來定製類的特殊方法
①基本定製型
C.__init__(self[,arg1,…]) 構造器(帶一些可選的參數)
C.__new__(self[,arg1,…])構造器(帶一些可選的參數);通常用在設置不變數據類型的子類
C.__del__(self) 解析器
C.__str__(self) 可打印的字符輸出;內建str()及print語句
C.__repr__(self) 運行時的字符串輸出;內建repr()和“操作符
C.__unicode__(self) Unicode字符串輸出:內建unicode()
C.__call__(self,*args) 表示可調用的實例
C.__nonzero__(self) 爲object定義False值;內建bool()
C.__len__(self) “長度”(可用於類);內建len()
②對象(值)比較
C.__cmp__(self,obj) 對象比較:內建cmp()
C.__lt__(self,obj) and C.__le__(self,obj) 小於/小於或等於:對應<及<=操作符
C.__gt__(self,obj) and C.__ge__(self,obj) 大於/大於或等於:對應>及>=操作符
C.__eq__(self,obj) and C.__ne__(self,obj) 等於/不等於:對應==,!=及<>操作符
③屬性
C.__getattr__(self,attr) 獲取屬性:內建getattr(),僅當屬性沒有找到時調用
C.__setattr__(self,attr,val) 設置屬性
# 特殊的兩個方法
'''
__getattr__(self,key),__setattr__(self,key,value)的注意事項
類繼承基類object中也包含了__getattr__ 和 __setattr__ 方法,如果類中沒有自定義這兩個方法,會執行object中的方法
'''
class Foo(object):
def __init__(self):
# self是Foo對象,self.storage 表示給對象中的屬性storage賦值,這樣就會先執行__setattr__
self.storage = {}
object.__setattr__(self,'storage',{})
# 通過父類給屬性賦值,這樣可以繞開自定義的__setattr__,率先執行創建賦值,執行後就能正常使用了
def __getattr__(self,item):
print(item)
def __setattr__(self,key,value):
'''
注意:在__init__中的屬性不能直接在__setattr__中直接引用,因爲運行__init__給屬性賦
值時,會先運行__setattr__創建屬相併賦值,所以在__setattr__獲取self.storage會報錯
'''
# print(self.storage)
print(key,value)
obj = Foo() # 執行了__init__
obj.xx = 123 # 執行了__setattr__
'''
輸出:
storage = {}
xx 123
'''
C.__delattr__(self,attr) 刪除屬性
C.__getattribute__(self,attr) 獲取屬性:內建getattr(),總是被調用
C.__get__(self,attr) (描述符)獲取屬性
C.__set__(self,attr,val) (描述符)設置屬性
C.__delete__(self,attr) (描述符)刪除屬性
④數值類型:二元操作符
C.__*add__(self,obj) 加:+操作符
C.__*sub__(self,obj) 減:-操作符
C.__*mul__(self,obj) 乘:*操作符
C.__*div__(self,obj) 除:/操作符
C.__*truediv__(self,obj) True除:/操作符
C.__*floordiv__(self,obj) Flooor除://操作符
C.__*mod__(self,obj) 取模/取餘:%操作符
C.__*divmod__(self,obj) 除和取模:內建divmod()
C.__*pow__(self,obj[,mod]) 乘冪:內建pow(),**操作符
⑤數值類型:二進制操作符
C.__*lshift__(self,obj) 左移位:<<操作符
C.__*rshift__(self,obj) 右移位:>>操作符
C.__*and__(self,obj) 按位與:&操作符
C.__*or__(self,obj) 按位或:|操作符
C.__*xor__(self,obj) 按位與或:^操作符
⑥數值類型:一元操作符
C.__neg__(self) 一元負
C.__pos__(self) 一元正
C.__abs__(self) 絕對值,內建abs()
C.__invert__(self) 按位求反,~操作符
⑦數值類型:數值轉換
C.__complex__(self,com) 轉爲complex(複數),內建complex()
C.__int__(self) 轉爲int,內建int()
C.__long__(self) 轉爲long,內建long()
C.__float__(self) 轉爲float,內建float()
⑧數值類型:基本表示法(String)
C.__oct__(self) 八進制表示,內建oct()
C.__hex__(self) 十六進制表示,內建hex()
⑨數值類型:數值壓縮
C.__coerce__(self,num) 壓縮成同樣的數值類型,內建coerce()
C.__index__(self) 在有必要時,壓縮可選的數值類型爲整型(比如用於切片索引等)
⑩序列類型
C.__len__(self) 序列中項的數目
C.__getitem__(self,ind) 得到單個序列
C.__setitem__(self,ind,val) 設置單個序列元素
C.__delitem__(self,ind) 刪除單個序列元素
C.__getslice__(self,ind1,ind2) 得到序列片段
C.__setslice__(self,ind1,ind2,val) 設置序列片段
C.__delslice__(self,ind1,ind2) 刪除序列片段
C.__contains__(self,val) 測試序列成員:內建in關鍵字
C.__*add__(self,obj) 串聯:+操作符
C.__*mul__(self,obj) 重複:*操作符
C.__iter__(self) 創建迭代器:內建iter()
⑪映射類型
C.__len__(self) mapping中項的數目
C.__hash__(self) 散列(hash)函數值
C.__getitem__(self,key) 得到給定鍵(key)的值
C.__setitem__(self,key,val) 設置給定鍵(key)的值
C.__delitem__(self,key) 刪除給定鍵(key)的值
C.__missing__(self,key) 給定鍵如果不存在字典中,則提供一個默認值
(2)簡單定製
自己實現init(),str(),repr()等。
print使用str()方法,真正的字符串對象表示使用repr()方法。
#! /usr/bin/env python
class RoundFloatManual(object):
def __init__(self,val):
assert isinstance(val,float),\
"Value must be a float!"
self.value=round(val,2)
def __str__(self):
return '%.2f' % self.value
__repr__=__str__
(3)數值定製
重載__add__()方法,就重載了(+)操作符。
還可以使用__radd__()方法和__iadd__()方法。
def __add__(self,other):
return self.__class__(self.hr+other.hr,self.min+other.min)
覆蓋“原位”操作,實現增量賦值(2.0),比如iadd()支持mon+=tue。
(4)定製迭代器
實現類中的__iter__()和next()方法來創建一個迭代器。
#! /usr/bin/env python
class AnyIter(object):
def __init__(self,data,safe=False):
self.safe=safe
self.iter=iter(data)
def __iter__(self):
return self
def next(self,howmany=1):
retval=[]
for eachItem in range(howmany):
try:
retval.append(self.iter.next())
catch StopIteration:
if self.safe:
break
else:
raise
return retval
12、私有化
類中屬性默認情況下是“公開的”,類所在模塊以及導入類所在模塊中的代碼都可以訪問到。
(1)雙下劃線
Python使用雙下劃線(__)來“混淆”屬性,不允許直接訪問。
混淆後的屬性,會在名字前面加上下劃線和類名,比如NumStr類中的__num屬性,被混淆後,用於訪問這個數據值的標識符就變成了self._NumStr__num。混淆操作可以防止在父類或子類中的同名衝突。
(2)單下劃線
使用單下劃線(_)實現簡單的模塊級私有化。
13、授權
(1)包裝
包裝任何類型作爲一個類的核心成員,使新對象的行爲模仿你想要的數據類型中已經存在的行爲,且去掉不希望存在的行爲。擴充Python是包裝的另一種形式。
(2)實現授權
授權是包裝的一個特性。
授權的過程即是所有更新的功能都是由新類的某部分來處理,但已存在的功能就授權給對象的默認屬性。實現授權的關鍵點是覆蓋__getattr__()方法,在代碼中包含一個對getattr()內建函數的調用。
class WrapMe(object):
def __init__(self,obj):
self.__data=obj
def get(self):
return self.__data
def __repr__(self):
return 'self.__data'
def __str__(self):
return str(self.__data)
def __getattr__(self,attr):
return getattr(self.__data,attr)
wrappedComplex=WrapMe(3.5+4j)
wrappedComplex.real
訪問real屬性時,Python解釋器將試着在局部名稱空間中查找那個名字;若沒有找到,會搜索類名稱空間,以一個類屬性訪問;若還沒有找到,搜素則對原對象開始授權請求,此時調用__getattr__()方法,__getattr__()方法中調用getattr()方法得到一個對象的默認行爲。
總結:通過覆蓋__getattr__()方法實現授權。
授權只能訪問屬性,特殊行爲不可以。例如對列表的切片操作,它內建於類型中,不是屬性,不能授權訪問。
屬性可以是數據屬性,還可以是函數或者方法。Python所有數值類型,只有複數擁有屬性:數據屬性和conjugate()內建方法。
wrappedList=WrapMe([123,'foo',45.67])
wrrapedList[3] #會拋出AttributeError
此時可以採用“作弊”的方法來訪問實際對象和它的切片能力。
realList=wrappedList.get() #get()方法取得對原對象的訪問
realList[3]
14、新式類的高級特性
(1)新式類的通用特性
類型和類的統一,使得可以子類化Python數據類型。同時,所有的Python內建的“casting”或轉換函數現在都是工廠函數。例如:int(),long(),float(),complex(),str(),unicode();list(),tuple();type()。
另外,還加入了一些新的函數: basestring();dict();bool();set(),frozenset();object();classmethod();staticmethod();super();property();file()。這些類名和工廠函數,不僅能創建這些類名的新對象,還可以用來作爲基類,去子類化類型。現在還可以用於isinstance()內建函數,isinstance()函數在obj是一個給定類型的實例或其子類的實例時返回True。
OLD(not as good):
if type(obj)==type(0)...
if type(obj)==types.IntType...
BETTER:
if type(obj) is type(0)...
EVEN BETTER:
if isinstance(obj,int)...
if isinstance(obj,(int,long))...
if type(obj) is int...
(2)__slots__類屬性
__dict__屬性跟蹤所有實例屬性。
實例inst,屬性foo,那麼inst.foo與inst.__dict__[‘foo’]等價。
字典會佔用大量內存,爲內存上的考慮,可用__slots__屬性替代__dict__。
__slots__是一個類變量,由一序列型對象組成。由所有合法標識構成的實例屬性的集合來表示。任何試圖創建一個其名不在__slots__中的名字的實例屬性都將導致AttributeError異常。帶__slots__屬性的類定義不會存在__dict__屬性了。使用__slots__屬性的目的是節約內存。使用__slots__屬性可以防止用戶隨心所欲的動態增加實例屬性。
class SlottedClass(object):
__slots__=('foo','bar')
c=SlottedClass()
c.foo=42
c.xxx='nihao' #引發AttributeError異常
(3)__getattribute__()、__getattr__()特殊方法
Python類有一個__getattr__()的特殊方法,僅當屬性不能在實例或類或祖先類的__dict__屬性中找到時,才被調用。__getattribute__()與__getattr__()類似,不同在於,當屬性被訪問時,它就一直可以被調用,而不侷限於不能找到的情況。在同時定義了__getattribute__()及__getattr__()方法的類中,除非明確從__getattribute__()方法調用,或者__getattribute__()方法引發了AttributeError異常,否則後者不會被調用。如果將要在__getattribute__()方法中訪問這個類或其祖先類的屬性,應該總是調用祖先類的同名方法,避免引起無窮遞歸。
(4)描述符(描述符就是可重用的屬性)
可認爲描述符是表示對象屬性的一個代理,它爲屬性提供了強大的API。當需要屬性時,可以通過描述符來訪問它(當然還可以使用常規的句點屬性標誌法來訪問屬性)。
__get__(),__set__(),__delete__()特殊方法分別用於得到一個屬性的值,對一個屬性進行賦值,刪除掉某個屬性。同時覆蓋__get__()和__set__()的類被稱作數據描述符。實現了__set__()方法的類被稱作非數據描述符,或方法描述符。
__get__(),__set__(),__delete__()的原型如下:
__get__(self,obj,typ=None)=>None
__set__(self,obj,val)=>None
__delete__(self,obj)=>None
整個描述符系統的心臟是__getattribute__()特殊方法,因爲對每個屬性的訪問都會調用這個特殊的方法。
舉例來說,給定類X和實例x:
訪問實例屬性,x.foo由__getattribute__()轉化成:
type(x).__dict__['foo'].__get__(x,type(x))
訪問類屬性,那麼None將作爲對象被傳入:
X.__dict__['foo'].__get__(None,X)
訪問父類屬性,super(Y,obj).foo(假設Y爲X的子類):
X.__dict__['foo'].__get__(obj,X)
靜態方法、類方法、屬性,甚至所有的函數都是描述符。Python中函數之間的唯一區別在於調用方式的不同,分爲綁定和非綁定狼類,函數描述符可以處理這些問題,描述符會根據函數的類型確定如何“封裝”這個函數和函數被綁定的對象,然後返回調用對象。使用描述符的順序很重要,有一些描述符的級別要高於其他的。描述符是一個類屬性,因此所有的類屬性皆具有最高的優先級。優先級排序:類屬性>數據描述符>實例屬性>非數據描述符>默認爲__getattr__()。
#! /usr/bin/env python
import os
import pickle
class FileDescr(object):
saved=[]
def __init__(self,name=None):
self.name=name
def __get__(self,obj,typ=None):
if self.name not in FileDescr.saved:
raise AtrributeError,"%r used before assignment" % self.name
try:
f=open(self.name,'r')
val=pickle.load(f)
f.close()
return val
except(pickle.UnpicklingError,IOError,EOFError,AttributeError,ImportError,IndexError),e:
raise AttributeError, "could not read %r" % self.name
def __set__(self,obj,val):
f=open(self.name,'w')
try:
pickle.dump(val,f)
FileDescr.saved.append(self.name)
except(TypeError,pickle.PickingError),e:
raise AttributeError, "could not pickle %r" % self.name
finally:
f.close()
def __delete__(self,obj):
try:
os.unlink(self.name)
FileDescr.saved.remove(self.name)
except(OSError,ValueError),e:
pass
class MyFileValClass(object):
foo=FileDescr('foo')
bar=FileDescr('bar')
fvc=MyFileVarClass()
print fvc.foo #引發AttributeError
fvc.bar=42
print fvc.bar #打印42
(5)property()內建函數
屬性是一種有用的特殊類型的描述符。屬性用來處理所有實例屬性的訪問。
使用句點符號訪問實例屬性,其實是在修改實例的__dict__屬性。
使用property()訪問實例屬性,使用的是函數(或方法)。
property(fget=None,fset=None,fdel=None,doc=None)
property()接受一些傳進來的函數作爲參數,property()是在它所在的類被創建時被調用的,傳進來的函數都是非綁定的。
class HideX(object):
def __init__(self,x):
assert isinstance(x,int),'"x" must be an integer!'
self.__x=~x
def get_x(self):
return ~self.__x
def set_x(self,x):
assert isinstance(x,int),'"x" must be an integer!'
self.__x=~x
x=property(get_x,set_x)
改進代碼==>
class AdvancedHideX(object):
def __init__(self,x):
assert isinstance(x,int),'"x" must be an integer!'
self.__x=~x
@property
def x():
def fget(self):
return ~self.__x
def fset(self,x):
assert isinstance(x,int),'"x" must be an integer!'
self.__x=~x
return locals()
改進後的代碼:類名稱空間更加簡潔;用戶不能通過inst.set_x(40)來給屬性賦值,只能使用init.x=4。
(6)元類和__metaclass__
元類是一個類(一個類中類),它的實例是其他的類。當創建一個新類時,就是在使用默認的元類,它是一個類型對象(傳統類的元類是types.ClassType).當某個類調用type()函數時,就會看到它到底是誰的實例。元類一般用於創建類,在執行類定義時,解釋器必須知道類正確的元類,所以解釋器會尋找類屬性的__metaclass__屬性,若沒找到,會向上查找父類的__metaclass__屬性,如果還沒找到,解釋器會檢查名字爲__metaclass__的全局變量,若還不存在,就用types.ClassType作爲此類的元類。在類定義時,將檢查此類正確的元類,元類通常傳遞三個參數到構造器:類名,從基類繼承數據的元組和(類的)屬性字典。通過定義一個元類可以“迫使”程序員按照某種方式實現目標類,這樣既可以簡化他們的工作,也可以使編寫出的程序更符合特定標準。
# coding=gbk
#! /usr/bin/env python
from warnings import warn
class ReqStrSugRepr(type):
def __init__(cls,name,bases,attrd):
super(ReqStrSugRepr,cls).__init__(name,bases,attrd)
if '__str__' not in attrd:
raise TypeError('Class requires overriding of __str__()')
if '__repr__' not in attrd:
warn('Class suggests overriding of __repr__()\n',stacklevel=3)
print '*** Defined ReqStrSugRepr (meta)class \n'
class Foo(object):
__metaclass__=ReqStrSugRepr
def __str__(self):
return 'Instance of class:',self.__class__.__name__
def __repr__(self):
return self.__class__.__name__
print '*** Defined Foo Class \n'
class Bar(object):
__metaclass__=ReqStrSugRepr
def __str__(self):
return 'Instance of class:',self.__class__.__name__
print '*** Defined Bar Class\n'
class FooBar(object):
__metaclass__=ReqStrSugRepr
print '*** Defined FooBar Class\n'
15.其他模塊
UserList 提供一個列表對象的封裝類
UserDict 提供一個字典對象的封裝類
UserString 提供一個字符串對象的封裝類
types 定義所有Python對象的類型再標準Python解釋器中的名字
operator 標準操作符的函數接口