Python 面向對象

面向對象概述:

    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 標準操作符的函數接口

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章