为什么有人会使用@property而不定义setter或deleter?
在Python代码中,我经常看到使用@property这个东西。
如果我理解得没错,使用property函数可以定义获取值、设置值和删除值的功能。
那么,如果没有定义设置值和删除值的功能(也就是没有使用@x.setter和@x.deleter),为什么还要用@property呢?这不是跟根本不使用@property一样吗?
4 个回答
简而言之
如果你的 @property
函数里有复杂的逻辑,要注意每次访问这个属性时,都会执行这些逻辑。因此,我建议使用一个带有获取器和设置器的方式。
详细说明
还有一个我觉得没有被充分讨论的点是,@property
作为一个获取器(getter),可能会被调用多次,而设置器(setter)通常只会在你创建对象时被调用一次。
在我看来,如果 @property
函数的工作量不太大,这种方式是可以使用的。在下面的例子中,我们只是把一些字符串拼接起来生成一个电子邮件地址。
class User:
DOMAIN = "boulder.com"
def __init__(self, first_name: str, last_name: str) -> None:
self.first_name = first_name
self.last_name = last_name
@property
def email(self) -> str:
return "{}_{}@{}".format(self.first_name, self.last_name, self.DOMAIN)
但是,如果你打算在这个函数里添加一些复杂或重的逻辑,那么我建议为它创建一个获取器,这样就只会执行一次。例如,假设我们需要检查电子邮件是否唯一,这个逻辑放在获取器里会更好,否则每次你想访问这个电子邮件时,都会执行一次检查唯一性的逻辑。
class User:
DOMAIN = "boulder.com"
def __init__(self, first_name: str, last_name: str) -> None:
self.first_name = first_name
self.last_name = last_name
@property
def email(self) -> str:
return self._email
@email.setter
def email(self) -> None:
proposed_email = "{}_{}@{}".format(self.first_name, self.last_name, self.DOMAIN)
if is_unique_email(proposed_email):
self._email = proposed_email
else:
random_suffix = get_random_suffix()
self._email = "{}_{}_{}@{}".format(
self.first_name, self.last_name, random_suffix, self.DOMAIN
)
在某些情况下,定义一个只有获取功能(getter)而没有设置功能(setter)的属性是非常有用的。比如说,在Django中,你有一个模型,模型其实就是一个数据库表,里面有一些叫做字段的条目。这里的hostname属性是从数据库中的一个或多个字段计算得来的。这样就避免了每次相关字段改变时,还需要在数据库表中再添加一个条目的麻烦。
使用属性的真正好处在于调用 object.hostname()
和 object.hostname
的区别。后者会自动和对象一起传递,所以当我们在像jinja模板这样的地方时,可以直接调用 object.hostname
,但如果调用 object.hostname()
就会出错。
下面的例子是一个虚拟机模型,它有一个名称字段,以及一个我们传递了虚拟机对象的jinja代码示例。
# PYTHON CODE
class VirtualMachine(models.Model):
name = models.CharField(max_length=128, unique=True)
@property
def hostname(self):
return "{}-{}.{}".format(
gethostname().split('.')[0],
self.name,
settings.EFFICIENT_DOMAIN
)
# JINJA CODE
...start HTML...
Name: {{ object.name }}
# fails
Hostname: {{ object.hostname() }}
# passes
Hostname: {{ object.hostname }}
...end HTML...
它创建了一个不允许设置值的接口。这在其他编程语言中类似于常量。