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

问题描述 投票:0回答:6

加快学习课程的进度。我一直在读到构造函数(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
是一个坏主意吗?

python class properties decorator python-decorators
6个回答
12
投票

不,您可以根据自己的喜好使用多个

@property
装饰器。显然,除了示例作者的想象力之外,这里没有任何限制。

Python 标准库充满了

@property
如果您想要示例,请使用:

  • numbers
    定义了 Python 中数字类的 ABC。

  • tempfile
    实现临时文件对象

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

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

等等

你说得对;多个属性看起来与您发布的完全一样。


3
投票

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

另一种模式是这样的:

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


2
投票

要跳到另一个答案,

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

确实不在 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}

我是这个库的作者,我写它是因为没有一个现有的替代方案令我满意。

请参阅文档了解详细信息 - 请随时提供反馈!


0
投票

它可能很难看,但这就是它的工作原理。一般来说,Pythonic 建议不要创建直接访问本机变量的简单 setter/getter(除非您知道接口可能会更改)。 假设属性的原因是“复杂的计算”,正如您所说,对于看起来有点丑陋的模式没有捷径。请参阅此链接http://tomayko.com/writings/getters-setters-fuxors


0
投票

是的,但是 @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 属性。


0
投票

您可以使用方法

__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)
© www.soinside.com 2019 - 2024. All rights reserved.