内置的property经常用作装饰器,但它其实是一个类。在Python中,函数和类通常可以互换,因为二者都是可调用对象,而且没有实例化的new运算符,所以调用构造方法和调用工厂函数没有区别,只要能返回新的可调用对象,代替被装饰的函数,二者都可用作装饰器
property初始化方法(__init__)的完整签名如下:
property(fget=None, fset=None, fdel=None, doc=None)
所有的参数都是可选的,如果没有把函数传给某个参数,那么得到的特性对象就不允许执行相应的操作
class LineItem:
def __init__(self, description, weight, price):
self.description = description
self.weight = weight
self.price = price
def subtotal(self):
return self.weight * self.price
def get_weight(self): # <1>
return self.__weight
def set_weight(self, value): # <2>
if value > 0:
self.__weight = value
else:
raise ValueError(‘value must be > 0‘)
weight = property(get_weight, set_weight) # <3>
测试LineItem这个类
line = LineItem("Golden raisins", 10, 6.5)
print("<1>:", line.subtotal()) # <1>
line.weight = -9
运行结果:
<1>: 65.0 Traceback (most recent call last): …… ValueError: value must be > 0
当然,我们可以写成另外一种形式,也是可以通过方法来读取和设置公开属性的
class LineItem:
def __init__(self, description, weight, price):
self.description = description
self.weight = weight
self.price = price
def subtotal(self):
return self.weight * self.price
@property
def weight(self):
return self.__weight
@weight.setter
def weight(self, value):
if value > 0:
self.__weight = value
else:
raise ValueError(‘value must be > 0‘)
特性都是类属性,但是特性管理其实是实例属性的存取,如果实例和所属的类有同名的属性,那么在获取实例读取属性时,实例属性会覆盖类属性
class Class:
data = "the class data attr"
@property
def prop(self):
return "the prop value"
obj = Class()
print("<1>:", vars(obj)) # <1>
print("<2>:", obj.data) # <2>
obj.data = "bar" # <3>
print("<4>:", vars(obj)) # <4>
print("<5>:", obj.data) # <5>
print("<6>:", Class.data) # <6>
运行结果:
<1>: {}
<2>: the class data attr
<4>: {‘data‘: ‘bar‘}
<5>: bar
<6>: the class data attr
下面,尝试覆盖obj的prop特性
print("<1>:", Class.prop) # <1>
print("<2>:", obj.prop) # <2>
obj.prop = "hello world"
运行结果:
<1>: <property object at 0x00000000004805E8>
<2>: the prop value
Traceback (most recent call last):
obj.prop = "hello world"
AttributeError: can‘t set attribute
obj.__dict__["prop"] = "hello world" # <1>
print("<2>:", vars(obj)) # <2>
print("<3>:", obj.prop) # <3>
Class.prop = "baz" # <4>
print("<5>:", obj.prop) # <5>
运行结果:
<2>: {‘prop‘: ‘hello world‘}
<3>: the prop value
<5>: hello world
定义一个特性工厂函数
我们将定义一个名为quantity的特性函数,替我们管理之前LineItem这个类中的weight和price这两个属性,要求这两个属性都必须大于0
def quantity(storage_name):
def qty_getter(instance): # <3>
return instance.__dict__[storage_name]
def qty_setter(instance, value): # <4>
if value > 0:
instance.__dict__[storage_name] = value
else:
raise ValueError(‘value must be > 0‘)
return property(qty_getter, qty_setter)
class LineItem:
weight = quantity(‘weight‘) # <1>
price = quantity(‘price‘)
def __init__(self, description, weight, price):
self.description = description
self.weight = weight # <2>
self.price = price
def subtotal(self):
return self.weight * self.price
测试工厂函数
nutmeg = LineItem("Moluccan nutmeg", 8, 13.95)
print("<1>:", nutmeg.weight, nutmeg.price) # <1>
print("<2>:", sorted(vars(nutmeg).items())) # <2>
运行结果:
<1>: 8 13.95 <2>: [(‘description‘, ‘Moluccan nutmeg‘), (‘price‘, 13.95), (‘weight‘, 8)]
定义特性时,可以使用@attr.deleter装饰器包装一个方法,负责删除特性管理的属性
class BlackKnight:
def __init__(self):
self.members = [‘an arm‘, ‘another arm‘,
‘a leg‘, ‘another leg‘]
self.phrases = ["‘Tis but a scratch.",
"It‘s just a flesh wound.",
"I‘m invincible!",
"All right, we‘ll call it a draw."]
@property
def member(self):
print(‘next member is:‘)
return self.members[0]
@member.deleter
def member(self):
text = ‘BLACK KNIGHT (loses {})\n-- {}‘
print(text.format(self.members.pop(0), self.phrases.pop(0)))
测试删除特性
b = BlackKnight() del b.member del b.member del b.member del b.member
运行结果:
BLACK KNIGHT (loses an arm) -- ‘Tis but a scratch. BLACK KNIGHT (loses another arm) -- It‘s just a flesh wound. BLACK KNIGHT (loses a leg) -- I‘m invincible! BLACK KNIGHT (loses another leg) -- All right, we‘ll call it a draw.
原文:https://www.cnblogs.com/beiluowuzheng/p/9222708.html