@属性 的优点假设我只需要获取值
Advantage of @property assuming I only need to get the value
我看了很多关于@property
的优点,我非常喜欢它们。作为一个简短的例子
class Foo:
def __init__(self):
self._foo_value = 'foo_value'
@property
def foo_value(self):
return self._foo_value
my_foo = Foo()
print(my_foo.foo_value)
但是,假设我知道我不想做花哨的东西,只获取 Foo.foo_value
的值,我可以用更少的代码实现相同的行为通过忽略 @property
,例如
class Foo:
def __init__(self):
self.foo_value = 'foo_value'
my_foo = Foo()
print(my_foo.foo_value)
在这种情况下仍然使用 @property
有什么好处吗?
没有优势。如果有的话,你只是在给自己制造包袱。
其他语言(例如 Java)实现“getters”和“setters”并具有 private
变量之类的东西。 Python中没有private
变量;您可以使用前导 _
来 指示 名称是私有的,甚至可以使用 __
来表示“名称修改”(但这仍然不会阻止确定的用户)。
有时候 @property
确实有一些很大的用处。一个非人为的例子在我的一个包装器库的配置 class 中。我知道 0
到 1
之外的值会导致另一个 API 调用崩溃,因此如果该调用失败,我会尝试阻止用户陷入混乱。无论如何,我实际上无法阻止他们这样做,但我可以尝试让 API 警告他们。
(旁注;我不建议函数名称应该大写,但这实际上是 API 调用将设置的一些 Java 属性的名称,所以这是为了保持一致性)
@property
def CLUSTER_REGRET(self):
return self._CLUSTER_REGRET
@CLUSTER_REGRET.setter
def CLUSTER_REGRET(self, value):
if not 0 <= value <= 1:
raise ValueError("CLUSTER_REGRET must be between 0 and 1")
self._CLUSTER_REGRET = value
No, there is absolutely no use for a property
here. The entire point of property
is to avoid boilerplate and maintain encapsulation.
A Pythonic class definition
Suppose you write a class:
class Circle:
def __init__(self, radius):
self.radius = radius
Great. Now, there's a bunch of client code out there like:
print(f"The radius is {some_circle.radius}")
An un-Pythonic class definition
Suppose, however, you want to make sure the radius is never set to a value below 0. Oh no!我们应该做什么? The philosophy in languages like Java is that you should have written boilerplate getters and setters like this in the first place:
class Circle:
def __init__(self, radius):
self.set_radius(radius)
def get_radius(self):
return self.radius
def set_radius(self, radius):
self._radius = radius
Then, all the client code would have been written like:
print(f"The radius is {some_circle.get_radius()}")
And we can just modify set_radius
to throw an appropriate error:
class Circle:
def __init__(self, radius):
self.set_radius(radius)
def get_radius(self):
return self.radius
def set_radius(self, radius):
if radius < 0:
raise ValueError("Radius must be positive")
self._radius = radius
All that boilerplate at the beginning was worth it because now we can make changes to the way we control access to our internal state without breaking client code.
But this isn't Java, this is Python
In Python, if and when we decide to control access, we can refactor our class without breaking client code!:
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, radius):
if radius < 0:
raise ValueError("Radius must be positive")
self._radius = radius
Because let's be honest. Most of the time we'd rather write a class like the first example, not the second example with the pre-emptive boilerplate getters and setters, and we like to write client code like:
pi * circle.radius**2
instead of
pi * circle.get_radius()**2
In Python, we can have our cake and eat it too.
An exception
There is one use case for a property
like in your code: we want to let the clients of our class retrieve a value but not set the value. So just like your example:
>>> class Foo:
... def __init__(self):
... self._foo_value = 'foo_value'
... @property
... def foo_value(self):
... return self._foo_value
...
>>> foo = Foo()
>>> foo.foo_value
'foo_value'
>>> foo.foo_value = 'something else'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
If he above is the behavior you want, the the property
is actually serving a purpose. Of course, this can be easily circumvented, but it does prevent "accidental" misuse of the API. And, we still get to keep the nicer-looking foo.foo_value
instead of the uglier foo.get_foo_value()
我看了很多关于@property
的优点,我非常喜欢它们。作为一个简短的例子
class Foo:
def __init__(self):
self._foo_value = 'foo_value'
@property
def foo_value(self):
return self._foo_value
my_foo = Foo()
print(my_foo.foo_value)
但是,假设我知道我不想做花哨的东西,只获取 Foo.foo_value
的值,我可以用更少的代码实现相同的行为通过忽略 @property
,例如
class Foo:
def __init__(self):
self.foo_value = 'foo_value'
my_foo = Foo()
print(my_foo.foo_value)
在这种情况下仍然使用 @property
有什么好处吗?
没有优势。如果有的话,你只是在给自己制造包袱。
其他语言(例如 Java)实现“getters”和“setters”并具有 private
变量之类的东西。 Python中没有private
变量;您可以使用前导 _
来 指示 名称是私有的,甚至可以使用 __
来表示“名称修改”(但这仍然不会阻止确定的用户)。
有时候 @property
确实有一些很大的用处。一个非人为的例子在我的一个包装器库的配置 class 中。我知道 0
到 1
之外的值会导致另一个 API 调用崩溃,因此如果该调用失败,我会尝试阻止用户陷入混乱。无论如何,我实际上无法阻止他们这样做,但我可以尝试让 API 警告他们。
(旁注;我不建议函数名称应该大写,但这实际上是 API 调用将设置的一些 Java 属性的名称,所以这是为了保持一致性)
@property
def CLUSTER_REGRET(self):
return self._CLUSTER_REGRET
@CLUSTER_REGRET.setter
def CLUSTER_REGRET(self, value):
if not 0 <= value <= 1:
raise ValueError("CLUSTER_REGRET must be between 0 and 1")
self._CLUSTER_REGRET = value
No, there is absolutely no use for a property
here. The entire point of property
is to avoid boilerplate and maintain encapsulation.
A Pythonic class definition
Suppose you write a class:class Circle:
def __init__(self, radius):
self.radius = radius
Great. Now, there's a bunch of client code out there like:
print(f"The radius is {some_circle.radius}")
An un-Pythonic class definition
Suppose, however, you want to make sure the radius is never set to a value below 0. Oh no!我们应该做什么? The philosophy in languages like Java is that you should have written boilerplate getters and setters like this in the first place:
class Circle:
def __init__(self, radius):
self.set_radius(radius)
def get_radius(self):
return self.radius
def set_radius(self, radius):
self._radius = radius
Then, all the client code would have been written like:
print(f"The radius is {some_circle.get_radius()}")
And we can just modify set_radius
to throw an appropriate error:
class Circle:
def __init__(self, radius):
self.set_radius(radius)
def get_radius(self):
return self.radius
def set_radius(self, radius):
if radius < 0:
raise ValueError("Radius must be positive")
self._radius = radius
All that boilerplate at the beginning was worth it because now we can make changes to the way we control access to our internal state without breaking client code.
But this isn't Java, this is Python
In Python, if and when we decide to control access, we can refactor our class without breaking client code!:
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, radius):
if radius < 0:
raise ValueError("Radius must be positive")
self._radius = radius
Because let's be honest. Most of the time we'd rather write a class like the first example, not the second example with the pre-emptive boilerplate getters and setters, and we like to write client code like:
pi * circle.radius**2
instead of
pi * circle.get_radius()**2
In Python, we can have our cake and eat it too.
An exception
There is one use case for a property
like in your code: we want to let the clients of our class retrieve a value but not set the value. So just like your example:
>>> class Foo:
... def __init__(self):
... self._foo_value = 'foo_value'
... @property
... def foo_value(self):
... return self._foo_value
...
>>> foo = Foo()
>>> foo.foo_value
'foo_value'
>>> foo.foo_value = 'something else'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
If he above is the behavior you want, the the property
is actually serving a purpose. Of course, this can be easily circumvented, but it does prevent "accidental" misuse of the API. And, we still get to keep the nicer-looking foo.foo_value
instead of the uglier foo.get_foo_value()