Python - 类定义中的多个@property 语句?

Posted

技术标签:

【中文标题】Python - 类定义中的多个@property 语句?【英文标题】:Python - multiple @property statements in class definition? 【发布时间】:2014-03-22 00:44:29 【问题描述】:

加快学习课程的进度。我一直在阅读构造函数(Python中的def init)应该只设置分配的变量,计算的实例属性应该通过属性设置。此外,使用 @property 优于 Java 风格的 getter/setter

好的,但是我见过的每个例子都只设置一个属性。假设我有一个具有三个复杂属性的对象,需要计算、查询等。你如何表示多个 @property getterssettersdeleters强>?这是另一个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 的类是个坏主意?

【问题讨论】:

【参考方案1】:

不,您可以随意使用多个 @property 装饰器。显然,除了示例作者的想象之外,这里没有限制。

Python 标准库充满了@property 的使用,如果你想要示例的话:

numbers 为 Python 中的数字类定义 ABC。

tempfile 实现临时文件对象

threading 提供更高级别的线程支持

urlparse 用于处理 URL 和查询字符串。

等等

你看中了它;多个属性看起来与您发布的完全一样。

【讨论】:

【参考方案2】:

这不是一个坏主意。这是一个常规的属性模式。

另一种模式是这样的:

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

【讨论】:

考虑只使用myObj.foomyObj.foo() 而不是myObj.set_foo()。人们对此有所不同,但一致认为 get_ 通常没有帮助,只会让东西看起来像 Java。 谢谢 我没有考虑初始化。所以我只想说 def __init__(self, a=None): self.a = a? 是的,那将是相同的,并且更加封装。 @ap 我修复了我的代码(在删除器和设置器前加下划线)。看到这些方法是受保护的,并且它们的唯一目的是用作 property() 构造函数的参数,因此它们并不意味着公开访问,它们只是一个示例来说明。我更喜欢 getter、setter 和 deleter 的属性装饰器模式。 赞成以更简洁的形式装饰一组属性访问器。有时较少的垂直代码会提高可读性,特别是如果您有像这样的单行访问器,可以排列为 4 行块加上前导注释。【参考方案3】:

它可能很难看,但这就是它的工作方式。一般来说,pythonic 建议不要创建直接访问本机变量的简单 setter/getter(除非您知道接口可能会更改)。假设属性的原因是您所说的“复杂计算”,那么看起来有些难看的模式没有捷径可走。看这个链接http://tomayko.com/writings/getters-setters-fuxors

【讨论】:

【参考方案4】:

是的,但是@property 装饰器使在一个类中定义多个属性变得更加整洁。 属性与属性不同。封装中使用的原因属性是因为 python 没有内置的访问修饰符。事实上,当我们在方法上方调用 @property 装饰器时,我们实例化了一个属性对象。属性对象是具有getsetdel方法的对象。属性可以在类中定义,而不需要在 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 属性。

【讨论】:

【参考方案5】:

要跳出另一个答案,

看起来有点难看的图案没有捷径

确实不在 python stdlib 中,但现在有一个名为 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

我是这个库的作者,我写它是因为没有一个existing alternatives 让我满意。

详情请参阅documentation - 请随时提供反馈!

【讨论】:

以上是关于Python - 类定义中的多个@property 语句?的主要内容,如果未能解决你的问题,请参考以下文章

python property()函数:定义属性

python内置装饰器@property

python装饰器之property

Python面向对象编程第17篇 property类

Python面向对象编程第17篇 property类

Python高级property属性详解