類屬性和對象屬性定義
Python一切皆對象(object),每個對象都可能有多個屬性(attribute)。Python的屬性有一套統一的管理方案。
對象的屬性可能來自於其類定義,叫做類屬性(class attribute)。類屬性可能來自類定義自身,也可能根據類定義繼承來的。
一個對象的屬性還可能是該對象實例定義的,叫做對象屬性(object attribute)。
對象的屬性儲存在對象的__dict__屬性中。
dir()返回的僅是對象的屬性的一個名字類表,而 __dict__ 返回的是一個字典,它的鍵(key)是屬性名,鍵值(value)是相應的屬性對象的數據值。
chicken類繼承自bird類,而>>> summer為chicken類的一個對象。
class bird(object): feather = True class chicken(bird): fly = False def __init__(self, age): self.age = age
>>> summer = chicken(2)
>>> print(bird.__dict__) 類屬性
{'__dict__': <attribute '__dict__' of 'bird' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'bird' objects>, 'feather': True, '__doc__': None}
>>> print(chicken.__dict__) 類屬性
{'fly': False, '__module__': '__main__', '__doc__': None, '__init__': <function __init__ at 0x2b91db476d70>}
>>> print(>>> summer.__dict__) 對象屬性
{'age': 2}
當我們有一個>>> summer對象的時候,分別查詢>>> summer對象、chicken類、bird類以及object類的屬性,就可以知道>>> summer對象所有的__dict__,就可以找到通過對象>>> summer可以調用和修改的所有屬性了。下面兩種屬性修改方法等效:
>>> summer.__dict__['age'] = 3
>>> summer.age = 5
即時生成屬性
同一個對象的不同屬性之間可能存在依賴關系。當某個屬性被修改時,我們希望依賴於該屬性的其他屬性也同時變化。這時,我們不能通過__dict__的方式來靜態的儲存屬性。Python提供了多種即時生成屬性的方法。其中一種稱為特性(property)。特性是特殊的屬性。比如我們為chicken類增加一個特性adult。當對象的age超過1時,adult為True;否則為False:
方案1 --> 稍顯麻煩
--------------------------------------------------------------------------------------
class chicken(bird):
fly = False
def __init__(self, age):
self.age = age
def getAdult(self):
if self.age > 1.0: self.adult = True
else: self.adult = False
--------------------------------------------------------------------------------------
獲得adult對象屬性的方法
>>> summer = chicken(2) #實例化
>>> summer.getAdult() #調用getAdult()方法,給adult屬性賦值
>>> summer.adult #獲取adult屬性
方案2 --> 使用三元選擇,實用性較低
--------------------------------------------------------------------------------------
class bird(object):
feather = True
class chicken(bird):
fly = False
def __init__(self, age):
self.age = age
self.adult = True if self.age > 1.0 else False
--------------------------------------------------------------------------------------
獲得adult對象屬性的方法
>>> summer = chicken(2) #實例化
>>> summer.adult #獲取adult屬性
方案3 使用property,實用性更強
--------------------------------------------------------------------------------------
class bird(object):
feather = True
class chicken(bird):
fly = False
def __init__(self, age):
self.age = age
def getAdult(self):
if self.age > 1.0: return True
else: return False
adult = property(getAdult)
--------------------------------------------------------------------------------------
獲得adult對象屬性的方法
>>> summer = chicken(2) #實例化
>>> summer.adult #獲取adult屬性
????
class num(object):
def __init__(self, value):
self.value = value
def getNeg(self):
return -self.value
def setNeg(self, value):
self.value = -value
def delNeg(self):
print("value also deleted")
del self.value
neg = property(getNeg, setNeg, delNeg, "I'm negative")
x = num(1.1)
print(x.neg)
x.neg = -22
print(x.value)
print(num.neg.__doc__)
del x.neg
上面的num為一個數字,而neg為一個特性,用來表示數字的負數。當一個數字確定的時候,它的負數總是確定的;而當我們修改一個數的負數時,它本身的值也應該變化。這兩點由getNeg和setNeg來實現。而delNeg表示的是,如果刪除特性neg,那么應該執行的操作是刪除屬性value。property()的最后一個參數("I'm negative")為特性negative的說明文檔。
方案4 使用特殊方法__getattr__
--------------------------------------------------------------------------------------
class bird(object):
feather = True
class chicken(bird):
fly = False
def __init__(self, age):
self.age = age
def __getattr__(self, name):
if name == 'adult':
if self.age > 1.0: return True
else: return False
else: raise AttributeError(name)
--------------------------------------------------------------------------------------
每個特性需要有自己的處理函數,而__getattr__可以將所有的即時生成屬性放在同一個函數中處理。__getattr__可以根據函數名區別處理不同的屬性。比如上面我們查詢屬性名male的時候,raise AttributeError。
(Python中還有一個__getattribute__特殊方法,用於查詢任意屬性。__getattr__只能用來查詢不在__dict__系統中的屬性)
__setattr__(self, name, value)和__delattr__(self, name)可用於修改和刪除屬性。它們的應用面更廣,可用於任意屬性。
即時生成屬性的其他方式
即時生成屬性還可以使用其他的方式,比如descriptor(descriptor類實際上是property()函數的底層,property()實際上創建了一個該類的對象)。有興趣可以進一步查閱。
類屬性和對象屬性的作用域
類屬性:
公有屬性,私有屬性,內置屬性
對象屬性:
公有屬性,私有屬性,內置屬性,方法變量,全局變量
根據屬性不同,起作用范圍也不同。如下為圖示和說明:
--------------------------------------------------------------------------------------
class MyClass(object):
"""類公有屬性:在類中使用self.var1調用,類外使MyClass.var1調用,
也可以通過實例調用 instance.var1"""
var1 = 'class public attr: var1'
"""類私有屬性:在類中使用self.__var2調用, 類外使用
MyClass._MyClass__var2調用,也可以通過實例調用instance._MyClass__var2"""
__var2 = 'class private attr: __var2'
def method(self):
"""在方法method內有效"""
var3 = 'method local attr: var3'
print(var3)
"""對象公有屬性:方法調用后生效,在類內方法間通過self.var4調用,
在類外通過instance.var4"""
self.var4 = 'object public attr: self.var4'
print(self.var4)
"""對象私有屬性:方法調用后生效, 在方法間內通過self.__var5調用,
類內其他方法不可調用,在類外通過instance.__var5"""
self.__var5 = 'object private attr: self.__var5'
print(self.__var5)
def method2(self):
print(self.var1)
print(self.__var2)
print(self.var4)
--------------------------------------------------------------------------------------
"""通過類調用類屬性"""
>>> MyClass.var1
'class public attr: var1'
>>> MyClass._MyClass__var2
'class private attr: __var2'
"""通過對象調用類屬性"""
>>> obj = MyClass()
>>> obj.var1
'class public attr: var1'
>>> obj._MyClass__var2
'class private attr: __var2'
>>> obj.var4
'object public attr: self.var4'
>>> obj._MyClass__var5
'object private attr: self.__var5'
總結
__dict__分層存儲屬性。每一層的__dict__只存儲該層新增的屬性。子類不需要重復存儲父類中的屬性。
即時生成屬性是值得了解的概念。在Python開發中,你有可能使用這種方法來更合理的管理對象的屬性。