7.1 KiB
如何使用 Python 属性装饰器?
原文:https://www.askpython.com/python/built-in-methods/python-property-decorator
又见面了!在本文中,我们将看看 Python property decorator。
Python 有一个非常有用的特性,叫做 decorators,它只是函数包装器的语法糖。这里,我们将关注属性装饰器,这是一种特殊类型的装饰器。
这个主题可能会让你有点困惑,所以我们将使用说明性的例子一步一步地介绍它。我们开始吧!
Python 属性装饰器基于什么?
属性装饰器基于内置的property()函数。这个函数返回一个特殊的property对象。
您可以在您的 Python 解释器中调用它并查看一下:
>>> property()
<property object at 0x000002BBD7302BD8>
这个property对象有一些额外的方法,用于获取和设置对象的值。它也有删除它的方法。
方法列表如下:
property().getterproperty().setterproperty().deleter
但这还不止于此!这些方法也可以用在其他对象上,它们本身也可以充当装饰者!
所以,举个例子,我们可以用property().getter(obj),它会给我们另一个属性对象!
所以,需要注意的是属性装饰器将使用这个函数,它有一些特殊的方法来读写对象。但是这对我们有什么帮助呢?
现在让我们来看看。
使用 Python 属性装饰器
要使用属性装饰器,我们需要将它包装在任何函数/方法周围。
这里有一个简单的例子:
@property
def fun(a, b):
return a + b
这与以下内容相同:
def fun(a, b):
return a + b
fun = property(fun)
所以在这里,我们用fun()包装property(),这正是装饰者所做的!
现在让我们举一个简单的例子,在一个类方法上使用属性装饰器。
考虑下面的类,没有任何修饰的方法:
class Student():
def __init__(self, name, id):
self.name = name
self.id = id
self.full_id = self.name + " - " + str(self.id)
def get_name(self):
return self.name
s = Student("Amit", 10)
print(s.name)
print(s.full_id)
# Change only the name
s.name = "Rahul"
print(s.name)
print(s.full_id)
输出
Amit
Amit - 10
Rahul
Amit - 10
这里,正如你所看到的,当我们只改变对象的name属性时,它对full_id属性的引用仍然没有更新!
为了确保每当name或id更新时full_id属性也会更新,一个解决方案是将full_id变成一个方法。
class Student():
def __init__(self, name, id):
self.name = name
self.id = id
def get_name(self):
return self.name
# Convert full_id into a method
def full_id(self):
return self.name + " - " + str(self.id)
s = Student("Amit", 10)
print(s.name)
# Method call
print(s.full_id())
s.name = "Rahul"
print(s.name)
# Method call
print(s.full_id())
输出
Amit
Amit - 10
Rahul
Rahul - 10
这里,我们已经通过将full_id转换成方法full_id()解决了我们的问题。
然而,这并不是解决这个问题的最好方法,因为您可能需要将所有这样的属性转换成一个方法,并将这些属性转换成方法调用。这不方便!
为了减少我们的痛苦,我们可以使用@property装饰器来代替!
想法是把full_id()做成一个方法,但是用@property把它封装起来。这样,我们将能够更新full_id,而不必将其视为函数调用。
我们可以直接这样做:s.full_id。注意这里没有方法调用。这是因为属性装饰器。
让我们现在就来试试吧!
class Student():
def __init__(self, name, id):
self.name = name
self.id = id
def get_name(self):
return self.name
@property
def full_id(self):
return self.name + " - " + str(self.id)
s = Student("Amit", 10)
print(s.name)
# No more method calls!
print(s.full_id)
s.name = "Rahul"
print(s.name)
# No more method calls!
print(s.full_id)
输出
Amit
Amit - 10
Rahul
Rahul - 10
事实上,这种方法现在奏效了!现在,我们不需要使用括号调用full_id。
虽然它仍然是一个方法,但属性装饰器屏蔽了它,并将其视为类的属性!现在名字没意义了吧!?
通过 setter 使用属性
在上面的例子中,这种方法是可行的,因为我们没有直接显式地修改full_id属性。默认情况下,使用@property会使该属性只读。
这意味着您不能显式更改该属性。
s.full_id = "Kishore"
print(s.full_id)
输出
---> 21 s.full_id = "Kishore"
22 print(s.full_id)
AttributeError: can't set attribute
显然,我们没有权限,因为该属性是只读的!
要让属性可写,还记得我们讲过的property().setter方法吗,也是 decorator?
原来我们可以使用@full_id.setter添加另一个full_id属性,使其可写。@full_id.setter将继承原来full_id的所有财产,所以我们可以直接添加!
但是,我们不能在 setter 属性中直接使用full_id属性。请注意,这将导致无限递归下降!
class Student():
def __init__(self, name, id):
self.name = name
self.id = id
def get_name(self):
return self.name
@property
def full_id(self):
return self.name + " - " + str(self.id)
@full_id.setter
def full_id(self, value):
# Infinite recursion depth!
# Notice that you're calling the setter property of full_id() again and again
self.full_id = value
为了避免这种情况,我们将在我们的类中添加一个隐藏属性_full_id。我们将修改@property装饰器来返回这个属性。
更新后的代码将如下所示:
class Student():
def __init__(self, name, id):
self.name = name
self.id = id
self._full_id = self.name + " - " + str(self.id)
def get_name(self):
return self.name
@property
def full_id(self):
return self._full_id
@full_id.setter
def full_id(self, value):
self._full_id = value
s = Student("Amit", 10)
print(s.name)
print(s.full_id)
s.name = "Rahul"
print(s.name)
print(s.full_id)
s.full_id = "Kishore - 20"
print(s.name)
print(s.id)
print(s.full_id)
输出
Amit
Amit - 10
Rahul
Amit - 10
Rahul
10
Kishore - 20
我们已经成功地让full_id属性拥有了 getter 和 setter 属性!
结论
希望这能让您更好地理解属性装饰器,以及为什么您可能需要使用类属性,比如_full_id。
这些隐藏的类属性(像_full_id)让我们很容易使用外部的full_id属性!
这正是现代开源项目中大量使用属性的原因。
它们让最终用户非常容易,也让开发人员很容易将隐藏属性与非隐藏属性分离开来!