# 問題
你想建立乙個新的擁有一些額外功能的例項屬性型別,比如型別檢查。
# 解決方案
如果你想建立乙個全新的例項屬性,可以通過乙個描述器類的形式來定義它的功能。下面是乙個例子:
# descriptor attribute for an integer type-checked attribute
class integer:
def __init__(self, name):
self.name = name
def __get__(self, instance, cls):
if instance is none:
return self
else:
return instance.__dict__[self.name]
def __set__(self, instance, value):
if not isinstance(value, int):
raise typeerror('expected an int')
instance.__dict__[self.name] = value
def __delete__(self, instance):
del instance.__dict__[self.name]
乙個描述器就是乙個實現了三個核心的屬性訪問操作(get, set, delete)的類, 分別為 __get__() 、__set__() 和 __delete__() 這三個特殊的方法。 這些方法接受乙個例項作為輸入,之後相應的操作例項底層的字典。
為了使用乙個描述器,需將這個描述器的例項作為類屬性放到乙個類的定義中。例如:
class point:
x = integer('x')
y = integer('y')
def __init__(self, x, y):
self.x = x
self.y = y
當你這樣做後,所有對描述器屬性(比如x或y)的訪問會被 __get__() 、__set__() 和 __delete__() 方法捕獲到。例如:
>>> p = point(2, 3)
>>> p.x # calls point.x.__get__(p,point)
>>> p.y = 5 # calls point.y.__set__(p, 5)
>>> p.x = 2.3 # calls point.x.__set__(p, 2.3)
traceback (most recent call last):
file "", line 1, in
file "descrip.py", line 12, in __set__
raise typeerror('expected an int')
typeerror: expected an int
作為輸入,描述器的每乙個方法會接受乙個操作例項。 為了實現請求操作,會相應的操作例項底層的字典(__dict__屬性)。 描述器的 self.name 屬性儲存了在例項字典中被實際使用到的key。
# 討論
描述器可實現大部分python類特性中的底層魔法, 包括 @classmethod 、@staticmethod 、@property ,甚至是 __slots__ 特性。
通過定義乙個描述器,你可以在底層捕獲核心的例項操作(get, set, delete),並且可完全自定義它們的行為。 這是乙個強大的工具,有了它你可以實現很多高階功能,並且它也是很多高階庫和框架中的重要工具之一。
描述器的乙個比較困惑的地方是它只能在類級別被定義,而不能為每個例項單獨定義。因此,下面的**是無法工作的:
# does not work
class point:
def __init__(self, x, y):
self.x = integer('x') # no! must be a class variable
self.y = integer('y')
self.x = x
self.y = y
同時,__get__() 方法實現起來比看上去要複雜得多:
# descriptor attribute for an integer type-checked attribute
class integer:
def __get__(self, instance, cls):
if instance is none:
return self
else:
return instance.__dict__[self.name]
__get__() 看上去有點複雜的原因歸結於例項變數和類變數的不同。 如果乙個描述器被當做乙個類變數來訪問,那麼 instance 引數被設定成 none 。 這種情況下,標準做法就是簡單的返回這個描述器本身即可(儘管你還可以新增其他的自定義操作)。例如:
>>> p = point(2,3)
>>> p.x # calls point.x.__get__(p, point)
>>> point.x # calls point.x.__get__(none, point)
描述器通常是那些使用到裝飾器或元類的大型框架中的乙個元件。同時它們的使用也被隱藏在後面。 舉個例子,下面是一些更高階的基於描述器的**,並涉及到乙個類裝飾器:
# descriptor for a type-checked attribute
class typed:
def __init__(self, name, expected_type):
self.name = name
self.expected_type = expected_type
def __get__(self, instance, cls):
if instance is none:
return self
else:
return instance.__dict__[self.name]
def __set__(self, instance, value):
if not isinstance(value, self.expected_type):
raise typeerror('expected ' + str(self.expected_type))
instance.__dict__[self.name] = value
def __delete__(self, instance):
del instance.__dict__[self.name]
def typeassert(**kwargs):
def decorate(cls):
for name, expected_type in kwargs.items():
# attach a typed descriptor to the class
setattr(cls, name, typed(name, expected_type))
return cls
return decorate
# example use
@typeassert(name=str, shares=int, price=float)
class stock:
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
最後要指出的一點是,如果你只是想簡單的自定義某個類的單個屬性訪問的話就不用去寫描述器了。 這種情況下使用property技術會更加容易。 當程式中有很多重複**的時候描述器就很有用了 (比如你想在你**的很多地方使用描述器提供的功能或者將它作為乙個函式庫特性)。
python建立例項屬性 Python學習筆記
這是學習廖雪峰老師python教程的學習筆記 1 概覽 1.1 例項繫結屬性 class student object def init self,name self.name name s student bob 建立例項 s s.score 90 為s新增乙個score屬性 1.2 類繫結屬性 ...
Python中建立例項屬性 二
儘管可以通過person類建立出xiaoming xiaohong等例項,但是這些例項看上除了位址不同外,沒有什麼其他不同。在現實世界中,區分例項xiaoming xiaohong要依靠他們各自的名字 性別 生日等屬性。如何讓每個例項擁有各自不同的屬性?由於python是動態語言,對每乙個例項,都可...
python 建立類屬性
類是模板,而例項則是根據類建立的物件。繫結在乙個例項上的屬性不會影響其他例項,但是,類本身也是乙個物件,如果在類上繫結乙個屬性,則所有例項都可以訪問類的屬性,並且,所有例項訪問的類屬性都是同乙個!也就是說,例項屬性每個例項各自擁有,互相獨立,而類屬性有且只有乙份。定義類屬性可以直接在 class 中...