1. 程式人生 > >Python: 淺淡Python中的屬性(property)

Python: 淺淡Python中的屬性(property)

-h format 有意思 _for hook 類添加屬性 eas style turn

起源:
項目過程中需要研究youtube_dl這個開源組件,翻閱其中對類的使用,對比c#及Delphi中實現,感覺Python屬性機制挺有意思。
區別與高級編程語言之單一入口,在類之屬性這一方面,它隨意的太多,以致於習慣了高級語言的嚴謹,對如此隨意心裏倒是有些不安。
也難怪,因為其數據類型弱限制性,往往一個函數返回了一個結果,追溯此返回值類型,有時需要費上許多工夫!
我不是隨意的人,但隨意起來我還真不是人,用在此處,頗為貼切:b

屬性,是對事物某種特性的抽象,面向對象編程中一個重要概念;區別於字段,它通常表示為字段的擴展,加以訪問與設置保護機制。
比如動物,它的顏色、重量,都可以說是它的屬性,此篇以一動物類來做例子,淺探其中屬性機制。

1、新式類和經典類(New-style classes)

Python 2.x默認類為經典類,而對屬性支持完全者為新式類,其區別請自行度娘。
Python 3.x默認即為新式類,不必顯式繼承於object類。
如下面代碼類之定義:

#此為經典類
class AnimalClassic:
    pass

#此為新式類,Python 2.2始支持
class Animal(object):
    pass

雖一object差別,但其內在機制,改變頗多。略一窺之,大體如下:

>>>print dir(AnimalClassic) 

返回:

[__doc__, __module__
]

>>>print dir(Animal)

返回:

[__class__, __delattr__, __dict__, __doc__, __format__, __getattribute__, __hash__, __init__, __module__, __new__, __reduce__, __reduce_ex__, __repr__, __setattr__, __sizeof__, __str__, __subclasshook__, __weakref__]

由此可知,其加入了諸多面向對象的支持;此節屬性知識,亦基於新式類構建。所以諸君用Python 2.x做開發,寫面向對象代碼時,切記要繼承於object類

2、隨意的屬性

通常,我們這樣定義屬性:

class Animal(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

即在實例初始化時,__init__函數中賦值,其中值可為實例訪問:

a = Animal(black dog, 3)
print Name:, a.name
print Age:, a.age
a.color = Black
print Color:, a.color

結果為:

Name: black dog
Age: 3
Color: Black

看到了吧?運行期,能動態為實例添加屬性,比如Color。但此方法所添加,只能作用於此實例,而無影響於類
怎麽限制它?用__slots__這個東西,比如,__slots__ = [‘name‘, ‘age‘],則類只能添加這兩個屬性,蛋疼,不好用
如果想要在運行期給類添加屬性,要用到MethodType這東西,比如:

def set_color(self, color):
    self.color = color
Animal.set_color = MethodType(set_color, None, Animal)

a1 = Animal(yellow dog, 3)
a1.set_color(Yellow)
print a1.color

……喔,有點淩亂,還是不好用!或者說,心裏沒譜兒。


[email protected]

習慣了高級語言的嚴謹,總想對屬性加以訪問控制,相對安全些,比如直接在__init__中定義公用屬性,從封裝性來說,它是不好的寫法。
屬性之訪問,它亦有機制,[email protected],其獲取、設置函數,須與屬性名一致。
@property可以把一個實例方法變成其同名屬性,以支持.號訪問,它亦可標記設置限制,加以規範,如下代碼:

class Animal(object):
    def __init__(self, name, age):
        self._name = name
        self._age = age
        self._color = Black

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        if isinstance(value, basestring):
            self._name = value
        else:
            self._name = No name

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, value):
        if value > 0 and value < 100:
            self._age = value
        else:
            self._age = 0
            # print ‘invalid age value.‘

    @property
    def color(self):
        return self._color

    @color.setter
    def color(self, value):
        self._color = value;
    

a = Animal(black dog, 3)
a.name = white dog
a.age = 300
print Name:, a.name
print Age:, a.age

這樣在設定值時候,總算有個判斷取舍,是不是好一些?
私有變量以_開頭,是種編碼約寫,當然也可以直接訪問它。
不過既如此寫,直接訪問是不推薦的。
若真要為私有變量,則加雙下劃線,比如__name,也一樣阻止不了訪問,但讓我們知道,它不想被直接用到,如下代碼:

class Animal(object):
    def __init__(self, name):
        self.__name = name

a = Animal(black dog)
print a._Animal__name

這種寫法當真好奇怪!
引廖雪峰言:總的來說就是,Python本身沒有任何機制阻止你幹壞事,一切全靠自覺。

4、property函數
它以一個函數形式,定義一個屬性,[email protected],或者就是它的的變異用法。
其原型為:

property(fget=None, fset=None, fdel=None, doc=None)

譬如上面Animal類,其可用此改為:

class Animal(object):
    def __init__(self, name, age):
        self._name = name
        self._age = age
        self._color = Black

    def get_name(self):
        return self._name

    def set_name(self, value):
        if isinstance(value, basestring):
            self._name = value
        else:
            self._name = No name

    name = property(fget=get_name, fset=set_name, fdel=None, doc=name of an animal)

    def get_age(self):
        return self._age

    def set_age(self, value):
        if value > 0 and value < 100:
            self._age = value
        else:
            self._age = 0
            # print ‘invalid age value.‘

    age = property(fget=get_age, fset=set_age, fdel=None, doc=name of an animal)
    

a = Animal(black dog, 3)
a.name = white dog
a.age = 3
print Name:, a.name
print Animal.name.__doc__
print Age:, a.age

其輸出結果一樣,看來只是寫法不同。

後記:
由此可見,Python作為一種解釋型語言,其簡單易用,但其隨意性也大。如此屬性篇,也只是闡述其中一點,其它方面須在使用中去學習留意。
習慣了語法嚴謹,編碼亦請以Pythonic的風格去編寫,python實在沒有多大限制,只要正確計算機都能夠編譯,代碼風格是給人看的,優雅總比零亂好。

如上面幾種屬性寫法,[email protected],相對簡潔一些

參考資料:

Built-in Functions
Python基礎:新式類的屬性訪問
Python深入03 對象的屬性

Python: 淺淡Python中的屬性(property)