Python學習筆記13:Python面向物件程式設計
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。該方法在例項被建立後,例項化呼叫返回這個例項之前被呼叫。
2、面向物件程式設計常用術語
- 抽象/實現:建模 現實化
- 封裝/介面
- 合成:聯合、聚合
- 繼承/派生
- 泛化/特化
- 多型
- 自省/反射
3、類
Python中,一切皆為物件。
下面是類的定義語法。
class ClassName(object):
'class documentation string'
class_suite
Python不支援純虛擬函式(像C++)或者抽象方法等。
替代方案:在基類方法中引發NotImplementedError異常。
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'
>>>foo.x
{2003:'poe2',2004:'valid path'}
>>>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()(2.2)
幫助程式設計師找出相應的父類,然後呼叫相關屬性。
super(type[,obj])返回type的父類,傳入obj引數進行父類繫結。
- obj是一個例項,isinstance(obj,type)必須返回True;
- obj是一個類或型別,issubclass(obj,type)必須返回True。
super(MyClass,self).__init__()
(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) 設定屬性
- 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、新式類的高階特性(2.2+)
(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__()特殊方法
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'
14.其他模組
- UserList 提供一個列表物件的封裝類
- UserDict 提供一個字典物件的封裝類
- UserString 提供一個字串物件的封裝類
- types 定義所有Python物件的型別再標準Python直譯器中的名字
- operator 標準操作符的函式介面
相關推薦
Python學習筆記13:Python面向物件程式設計
1、引言 (1)類和例項:類是物件的定義,例項是“真正的實物”。 定義類:類名通常大寫字母打頭。 class MyNewObjectType(bases): 'define MyNewObjectType class' class_su
python學習筆記(二)面向物件程式設計
面向物件程式設計 self代表類的例項 python支援多重繼承 特殊方法def init(self,): 作用:在init方法內部,就可以把各種屬性繫結到self 前後分別有兩個下劃線
Python 學習筆記(五)[面向物件]
變數 類的變數 class A: num = 1 a = A() # 1 a.num # 1 A.num # 修改類的變數值 A.num = 2 # 2 a.num # 修改物件的變數值 a.num = 3 2 A.num # 給物件新增屬性 a.age
Python學習筆記13:標準庫之子程序(subprocess包)
ubprocess包主要功能是執行外部的命令和程式。從這個意義上來說,subprocess的功能與shell類似。 subprocess以及常用的封裝函式 當我們執行python的時候,我們都是在建立並執行一個程序。 在Python中,我們通過標準庫中的subprocess
Python學習筆記13:selenium webdriver 實現驗證碼登入
#通過ActionChains+autoit進行驗證碼圖片的下載,通過pytesseract識別驗證碼中的字元,由於有一定錯誤率,進行重試直至成功from selenium import webdriver from LanternAnswer.login import Lo
Python學習筆記4:一切皆物件,所有物件都是第一類的
一切都是物件 在python中下列語句其實都是一個共同點: i = 1 s = "abcde" def foo(): pass class C(object): pass instance = C() l = [1,2] t = (1,2) 他們在p
Python學習筆記4:Python物件
1、Python物件 所有Python物件都擁有三個特性:身份(只讀,id())、型別(只讀,type())和值。 Python型別也是物件。物件的值是否可更改被稱為物件的可改變性(mutabili
Python學習筆記01:Python解釋器
3.6 str 2.6 python安裝 blog 時間比較 info ima style 資料參考:廖雪峰的官方網站https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac927076
Python學習筆記二:Python基礎
Python語法採用縮排形式,有以下幾點需要注意: 註釋以#開頭; 當語句以冒號:結尾時,縮排的語句視為程式碼塊; 始終堅持4個空格的縮排; 大小寫敏感; 1. 資料型別和變數 1.1 資料型別 1.1.1 整數 在程式中的寫法和數學中的寫法一樣
Python學習筆記19:函數語言程式設計
import shutil#copy()複製檔案#格式: shutil.copy(來源路徑,目標路徑)#返回值:返回目標路徑#拷貝的同時,可以給檔案重新命名rst = shutil.copy('/home/dadada/hahaha.huhu', '/home/dadada/hahaha.hoho')prin
Python學習筆記3:Python基礎
1、語句和語法 (1)註釋(#) (2)繼續( \) 存在兩種例外情況,一個語句不使用反斜線也可以跨行: 在使用閉合操作符(小括號,中括號,花括號)時單一語句可跨多行; 使用三引號包括下的字串
Python學習筆記一:Python中列表元素轉為數字
numbers = ['1', '5', '10', '8']; 方法一: numbers = [ int(x) for x in numbers ] 方法二: umbers = list(map(int, numbers))
【學習筆記13】java面向物件-強制型別轉換
目前多型情況下不能訪問子類特有的成員。 如果需要訪問子類特有的成員,那麼需要進行型別強制轉換. 基本資料型別的轉換小資料型別-------->大的資料型別 自動型別轉換大資料型別--------->小資料型別 強制型別轉換 小資料型
php學習筆記(二)面向物件程式設計
public 公有的:本類,子類,外部物件都可以呼叫 protected 受保護的:本類 子類,可以執行,外部物件不可以呼叫 private 私有的:只能本類執行,子類與外部物件都不可呼叫 面向物件程式設計的三大特點 1)封閉性 封閉性,也可以稱為資訊隱藏。就是將一個
python學習筆記9:面向對象編程,類
數據 相同屬性 技術 -o 必須 是把 oop ack 繼承 一、面向對象編程 面向對象--Object Oriented Programming,簡稱oop,是一種程序設計思想。 還有另一種程序設計思想——面向過程編程。面向過程的思想是把一個項目、一件事情按照一定
python學習筆記2:字符串
nbsp 大小 alpha .com format 大小寫 fin 判斷大小 key python學習筆記2:字符串 總結:字符串是不可變變量,不能通過下標修改其值 字符串的方法都不會改變字符串原來的值,而是新生成一個字符串 一、3種寫法——單引號,雙引號,三引號
Python學習筆記六:文件處理
alt 筆記 lin 系統 顯式 當前位置 open 刷新 大小 一:打開文件 open(name,mode,[bufferSize]) name:文件路徑 mode:文件打開方式 二:文件讀取 read()方法:可以一次讀取文件的全部內容,Python把內容讀到
Python學習筆記3:簡單文件操作
name n) popu 元素 close nes pla () eof # -*- coding: cp936 -*- # 1 打開文件 # open(fileName, mode) # 參數:fileName文件名稱 # mode打開方式 # w
python學習筆記9:正則表達式
使用 引入 常用 常用的正則表達式 需要 style pan 表達式 span 一、簡介 正則表達式就是用來查找字符串的;用來匹配一些比較復雜的字符串。 使用正確表達式需要引入re模塊 (regular定期的有規律的) 二、匹配字符串的方法
python學習筆記8:異常處理
mage 可能 str 裏的 tro 信息 學習筆記 異常信息 常見 一、異常處理 在程序運行過程中,總會遇到各種各樣的錯誤。 程序一出錯就停止運行了,那我們不能讓程序停止運行吧,這時候就需要捕捉異常了,通過捕捉到的異常,我們再去做對應的處理。 如下,寫段代碼,