加快学习课程的进度。我一直在读到构造函数(Python 中的 def init)应该只设置分配的变量,计算的实例属性应该通过属性设置。此外,使用 @property 优于 Java 风格的 getter/setter。
好的,但是我见过的每个例子都只设置一个属性。假设我有一个具有三个复杂属性的对象,需要计算、查询等。如何表示多个 @property getters、setters、deleters?这是另一个post的示例:
class C(object):
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
所以,如果我有三个实例变量,它们是根据其他一些属性计算得到的值,它会像这样吗
class C(object):
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
@property
def y(self):
"""I'm the 'y' property."""
return self._y
@y.setter
def y(self, value):
self._y = value
@y.deleter
def y(self):
del self._y
@property
def z(self):
"""I'm the 'z' property."""
return self._z
@z.setter
def z(self, value):
self._z = value
@z.deleter
def z(self):
del self._z
或者我只看到一个
@property
陈述这一事实意味着一堂课有多个 @property
是一个坏主意吗?
这不是一个坏主意。这是一个常规的属性模式。
另一种模式是这样的:
class A(object):
def _get_a(self):
return self._a
def _set_a(self, v)
self._a = v
def _del_a(self)
del self._a
a = property(_get_a, _set_a, _del_a)
结果与第一个示例相同,您可以随意使用它。在构造函数中初始化 self._a 是一个好主意,否则,在调用 self.a 之前访问它会引发 AttributeError
要跳到另一个答案,
对于看起来有点难看的图案没有捷径
pyfields
的库可以以更简洁的方式执行此操作,当您不需要验证器而不是转换器时,不会牺牲速度。
from pyfields import field, init_fields
class C(object):
x = field(default=None, doc="the optional 'x' property")
y = field(doc="the mandatory 'y' property")
z = field(doc="the mandatory 'z' property")
@init_fields
def __init__(self):
pass
c = C(y=1, z=2)
print(vars(c))
产量
{'z': 2, 'y': 1, 'x': None}
我是这个库的作者,我写它是因为没有一个现有的替代方案令我满意。
请参阅文档了解详细信息 - 请随时提供反馈!
它可能很难看,但这就是它的工作原理。一般来说,Pythonic 建议不要创建直接访问本机变量的简单 setter/getter(除非您知道接口可能会更改)。 假设属性的原因是“复杂的计算”,正如您所说,对于看起来有点丑陋的模式没有捷径。请参阅此链接http://tomayko.com/writings/getters-setters-fuxors
是的,但是 @property 装饰器使在类中定义多个属性变得更加整洁。 属性与属性不同。封装中使用属性的原因是因为Python没有内置的访问修饰符。事实上,当我们在方法之上调用 @property 装饰器时,我们实例化了一个属性对象。属性对象是具有 get、set 和 del 方法的对象。属性可以在类中定义,而不需要在init中定义。实例化对象时调用 init 方法。如果在创建对象时需要设置私有属性,则在init中定义它,但是当我们不需要初始值时,我们不需要定义属性。这是没有属性的属性的示例。
class C(object):
#X Property
@property
def x(self):
"""I'm the 'x' property."""
print('Get The X Property')
return self._x
@x.setter
def x(self, value):
print('X Property Setted to {}'.format(value))
self._x = value
@x.deleter
def x(self):
print('X is Killed !')
del self._x
#Y Property
@property
def y(self):
"""I'm the 'y' property."""
print('Get The Y Property')
return self._y
@y.setter
def y(self, value):
print('Y Property Setted to {} '.format(value))
self._y = value
@y.deleter
def y(self):
print('Y is Killed !')
del self._y
当我们需要将x和y定义为类C的属性时。只需在init中设置该属性即可。因此,x 和 y 列为 C 类属性,并在对象实例化时设置初始值。
class C(object):
def __init__(self):
self._x = 0
self._y = 0
#X Property
@property
def x(self):
"""I'm the 'x' property."""
print('Get The X Property')
return self._x
@x.setter
def x(self, value):
print('X Property Setted to {}'.format(value))
self._x = value
@x.deleter
def x(self):
print('X is Killed !')
del self._x
#Y Property
@property
def y(self):
"""I'm the 'y' property."""
print('Get The Y Property')
return self._y
@y.setter
def y(self, value):
print('Y Property Setted to {} '.format(value))
self._y = value
@y.deleter
def y(self):
print('Y is Killed !')
del self._y
差异很简单。如果没有任何初始值,C 类就没有 x 和 y 属性。因此,如果我们尝试获取该属性,则会引发异常。有了初始值,类 C 从一开始就已经具有 X 和 Y 属性。
您可以使用方法
__setattr__
和__getattr__
:
class Properties:
def __init__(self, **kwargs):
self._attrs = kwargs
def __getattr__(self, name):
if name in self._attrs:
return self._attrs[name]
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
def __setattr__(self, name, value):
if name != "_attrs" and "_attrs" in self.__dict__ and name in self._attrs:
self._attrs[name] = value
else:
super().__setattr__(name, value)
# Init
obj = Properties(field1=88, field2=99)
# Print attributes
print(obj.field1)
print(obj.field2)
# Change attributes
obj.field1 = 888
obj.field2 = 333
print(obj.field1)
print(obj.field2)
# Error:
print(obj.field3)