使用 getter 和 setter 的 pythonic 方式是什么?

681 投票
9 回答
582478 浏览
提问于 2025-04-15 21:31

我这样做:

def set_property(property,value):  
def get_property(property):  

或者

object.property = value  
value = object.property

在Python中,使用获取器和设置器的正确方式是什么?

9 个回答

34
In [1]: class test(object):
    def __init__(self):
        self.pants = 'pants'
    @property
    def p(self):
        return self.pants
    @p.setter
    def p(self, value):
        self.pants = value * 2
   ....: 
In [2]: t = test()
In [3]: t.p
Out[3]: 'pants'
In [4]: t.p = 10
In [5]: t.p
Out[5]: 20

当然可以!请把你想要翻译的内容发给我,我会帮你用简单易懂的语言解释清楚。

593

在Python中使用getter和setter的正确方式是什么?

在Python中,正确的做法是使用“getter”和“setter”,而是直接使用普通的属性,就像问题中展示的那样,删除属性时可以用del(不过为了保护无辜者,名字被改了...内置函数):

value = 'something'

obj.attribute = value  
value = obj.attribute
del obj.attribute

如果以后你想修改获取和设置的方式,可以通过使用property装饰器来做到这一点,而不需要改动用户的代码:

class Obj:
    """property demo"""
    #
    @property            # first decorate the getter method
    def attribute(self): # This getter method name is *the* name
        return self._attribute
    #
    @attribute.setter    # the property decorates with `.setter` now
    def attribute(self, value):   # name, e.g. "attribute", is the same
        self._attribute = value   # the "value" name isn't special
    #
    @attribute.deleter     # decorate with `.deleter`
    def attribute(self):   # again, the method name is the same
        del self._attribute

(每次使用装饰器时,都会复制并更新之前的属性对象,所以要注意每个设置、获取和删除的函数/方法要用相同的名字。)

在定义了上述内容后,原来的设置、获取和删除代码还是一样的:

obj = Obj()
obj.attribute = value  
the_value = obj.attribute
del obj.attribute

你应该避免这样做:

def set_property(property,value):  
def get_property(property):  

首先,上面的代码不工作,因为你没有为属性提供一个实例参数(通常是self),应该是:

class Obj:

    def set_property(self, property, value): # don't do this
        ...
    def get_property(self, property):        # don't do this either
        ...

其次,这样做重复了两个特殊方法__setattr____getattr__的功能。

第三,我们还有setattrgetattr这两个内置函数。

setattr(object, 'property_name', value)
getattr(object, 'property_name', default_value)  # default is optional

@property装饰器是用来创建getter和setter的。

例如,我们可以修改设置的行为,给设置的值加上限制:

class Protective(object):

    @property
    def protected_value(self):
        return self._protected_value

    @protected_value.setter
    def protected_value(self, value):
        if acceptable(value): # e.g. type or range check
            self._protected_value = value

一般来说,我们希望避免使用property,直接使用属性就好。

这是Python用户所期待的。遵循最少惊讶原则,你应该尽量给用户他们所期待的东西,除非你有非常强烈的理由去做相反的事情。

示例

比如说,我们需要对象的保护属性是一个在0到100之间的整数,并且防止被删除,同时给用户适当的提示信息,告诉他们如何正确使用:

class Protective(object):
    """protected property demo"""
    #
    def __init__(self, start_protected_value=0):
        self.protected_value = start_protected_value
    # 
    @property
    def protected_value(self):
        return self._protected_value
    #
    @protected_value.setter
    def protected_value(self, value):
        if value != int(value):
            raise TypeError("protected_value must be an integer")
        if 0 <= value <= 100:
            self._protected_value = int(value)
        else:
            raise ValueError("protected_value must be " +
                             "between 0 and 100 inclusive")
    #
    @protected_value.deleter
    def protected_value(self):
        raise AttributeError("do not delete, protected_value can be set to 0")

(注意__init__中提到的是self.protected_value,而属性方法中提到的是self._protected_value。这样做是为了确保__init__通过公共API使用这个属性,确保它是“受保护的”。)

使用示例:

>>> p1 = Protective(3)
>>> p1.protected_value
3
>>> p1 = Protective(5.0)
>>> p1.protected_value
5
>>> p2 = Protective(-5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
  File "<stdin>", line 15, in protected_value
ValueError: protectected_value must be between 0 and 100 inclusive
>>> p1.protected_value = 7.3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 17, in protected_value
TypeError: protected_value must be an integer
>>> p1.protected_value = 101
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 15, in protected_value
ValueError: protectected_value must be between 0 and 100 inclusive
>>> del p1.protected_value
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 18, in protected_value
AttributeError: do not delete, protected_value can be set to 0

名字重要吗?

是的,重要.setter.deleter会复制原来的属性。这允许子类正确地修改行为,而不改变父类中的行为。

class Obj:
    """property demo"""
    #
    @property
    def get_only(self):
        return self._attribute
    #
    @get_only.setter
    def get_or_set(self, value):
        self._attribute = value
    #
    @get_or_set.deleter
    def get_set_or_delete(self):
        del self._attribute

现在为了让这个工作,你必须使用相应的名字:

obj = Obj()
# obj.get_only = 'value' # would error
obj.get_or_set = 'value'  
obj.get_set_or_delete = 'new value'
the_value = obj.get_only
del obj.get_set_or_delete
# del obj.get_or_set # would error

我不太确定这有什么用,但这种情况适用于你想要一个只读、只写或只删除的属性。最好保持语义相同的属性使用相同的名字。

总结

从简单的属性开始。

如果以后需要关于设置、获取和删除的功能,可以通过property装饰器来添加。

避免使用以set_...get_...开头的函数——这就是属性的用途。

1100

试试这个:Python 属性

下面是示例代码:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        print("getter of x called")
        return self._x

    @x.setter
    def x(self, value):
        print("setter of x called")
        self._x = value

    @x.deleter
    def x(self):
        print("deleter of x called")
        del self._x


c = C()
c.x = 'foo'  # setter called
foo = c.x    # getter called
del c.x      # deleter called

撰写回答