2.1.2.4 标准类库中的实例
首先,应该说明,在标准类库中有一些有用的修饰器。有三类装饰器确实构成了语言的一部分:
- classmethod 造成函数成为“类方法”,这意味着不需要创建类的实例就可以激活它。当普通的方法被激活后,解释器将插入一个实例对象作为第一个位置参数,
self
。当类方法被激活后,类自身被作为一点参数,通常称为cls
。
类方法仍然可以通过类的命名空间访问,因此,他们不会污染模块的命名空间。类方法可以用来提供替代的构建器:
In [1]:
class Array(object):
def __init__(self, data):
self.data = data
@classmethod
def fromfile(cls, file):
data = numpy.load(file)
return cls(data)
这是一个清洁器,然后使用大量的标记来`__init__`。
staticmethod用来让方法“静态”,即,从根本上只一个普通的函数,但是可以通过类的命名空间访问。当函数只在这个类的内部需要时(它的名字应该与_为前缀),或者当我们想要用户认为方法是与类关联的,尽管实施并不需要这样。
property是对getters和setters pythonic的答案。用
property
修饰过的方法变成了一个getter,getter会在访问属性时自动调用。
In [2]:
class A(object):
@property
def a(self):
"an important attribute"
return "a value"
In [3]:
A.a
Out[3]:
<property at 0x104139260>
In [4]:
A().a
Out[4]:
'a value'
在这个例子中,A.a
是只读的属性。它也写入了文档:help(A)
包含从getter方法中拿过来的属性的文档字符串。将a
定义为一个属性允许实时计算,副作用是它变成只读,因为没有定义setter。
要有setter和getter,显然需要两个方法。从Python 2.6开始,下列语法更受欢迎:
In [5]:
class Rectangle(object):
def __init__(self, edge):
self.edge = edge
@property
def area(self):
"""Computed area.
Setting this updates the edge length to the proper value.
"""
return self.edge**2
@area.setter
def area(self, area):
self.edge = area ** 0.5
这种方式有效是因为property
修饰器用property对象替换getter方法。这个对象反过来有三个方法,getter
、setter
和deleter
,可以作为修饰器。他们的任务是设置property对象的getter、 setter和deleter(存储为fget
、fset
和fdel
属性)。当创建一个对象时,getter可以像上面的例子中进行设置。当定义一个setter,我们已经在area
下有property对象,我们通过使用setter方法为它添加setter。所有的这些发生在我们创建类时。
接下来,当类的实例被创建后,property对象是特别的,当解释器执行属性访问,属性赋值或者属性删除时,任务被委托给property对象的方法。
为了让每个事情都清晰,让我们定义一个“debug”例子:
In [6]:
class D(object):
@property
def a(self):
print "getting", 1
return 1
@a.setter
def a(self, value):
print "setting", value
@a.deleter
def a(self):
print "deleting"
In [7]:
D.a
Out[7]:
<property at 0x104139520>
In [8]:
D.a.fget
Out[8]:
<function __main__.a>
In [9]:
D.a.fset
Out[9]:
<function __main__.a>
In [10]:
D.a.fdel
Out[10]:
<function __main__.a>
In [12]:
d = D() # ... varies, this is not the same `a` function
d.a
getting 1
Out[12]:
1
In [13]:
d.a = 2
setting 2
In [14]:
del d.a
deleting
In [15]:
d.a
getting 1
Out[15]:
1
属性是修饰语语法的极大扩展。修饰器语法的一个前提-名字不可以重复-被违背了,但是,到目前位置没有什么事变糟了。为getter、setter和deleter方法使用相同的名字是一个好风格。
一些更新的例子包括:
- functools.lru_cache 记忆任意一个函数保持有限的arguments\:answer对缓存(Python 3.2)
- functools.total_ordering是一类修饰器,根据单一的可用方法(Python 2.7)补充缺失的顺序方法(lt, gt, le, …)。