关于python:使用getter和setter的python方法是什么?

What's the pythonic way to use getters and setters?

我这样做就像:

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

1
2
object.property = value  
value = object.property

我对python还不熟悉,所以我仍在探索语法,我希望在这方面有一些建议。


试试这个:python属性

示例代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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


What's the pythonic way to use getters and setters?

"pythonic"的方法不是使用"getter"和"setters",而是使用简单的属性(如问题所示),以及使用del来取消引用(但更改名称是为了保护无辜的人……建筑:

1
2
3
4
5
value = 'something'

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

如果以后要修改设置并获取,则可以使用property修饰器在不更改用户代码的情况下进行修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Obj:
   """property demo"""
    #
    @property
    def attribute(self): # implements the get - this name is *the* name
        return self._attribute
    #
    @attribute.setter
    def attribute(self, value): # name must be the same
        self._attribute = value
    #
    @attribute.deleter
    def attribute(self): # again, name must be the same
        del self._attribute

(每个装饰器复制并更新先前的属性对象,因此请注意,对于每个集合、get和delete函数/方法,您可能应该使用相同的名称。)

定义以上内容后,原始设置、获取和删除是相同的:

1
2
3
4
obj = Obj()
obj.attribute = value  
the_value = obj.attribute
del obj.attribute

您应该避免这样做:

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

首先,上述方法不起作用,因为您没有提供将属性设置为(通常为self的实例)的参数,这将是:

1
2
3
4
5
6
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内置功能。

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

@property装饰器用于创建getter和setter。

例如,我们可以修改设置行为来限制正在设置的值:

1
2
3
4
5
6
7
8
9
10
    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用户所期望的。遵循最不令人惊讶的规则,除非你有一个非常令人信服的相反理由,否则你应该尝试给你的用户他们所期望的。

示范

例如,假设我们需要我们的对象的protected属性是一个介于0和100之间(包括0和100)的整数,并防止删除它,并使用适当的消息通知用户它的正确用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Protective(object):
    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")

用途:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
>>> 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复制了原物。这允许子类在不改变父类行为的情况下正确地修改行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

现在,要使其生效,您必须使用各自的名称:

1
2
3
4
5
6
7
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

我不确定这在哪里有用,但是用例是如果您想要一个get、set和/或delete-only属性。最好还是坚持语义上相同的同名属性。

结论

从简单的属性开始。

如果以后需要有关设置、获取和删除的功能,可以使用属性修饰器添加它。

避免使用名为set_...get_...的函数-这就是属性的用途。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

查看@property装饰器。


使用@property@attribute.setter不仅可以帮助您使用"pythonic"方式,而且可以在创建对象和更改对象时检查属性的有效性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person(object):
    def __init__(self, p_name=None):
        self.name = p_name

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, new_name):
        if type(new_name) == str: #type checking for name property
            self._name = new_name
        else:
            raise Exception("Invalid value for name")

通过这种方式,您实际上可以对客户端开发人员"隐藏"_name属性,还可以对名称属性类型进行检查。注意,即使在初始化过程中,也要遵循这种方法来调用setter。所以:

1
p = Person(12)

将导致:

1
Exception: Invalid value for name

但是:

1
2
3
4
5
6
7
>>>p = person('Mike')
>>>print(p.name)
Mike
>>>p.name = 'George'
>>>print(p.name)
George
>>>p.name = 2.3 # Causes an exception

你可以使用魔法方法__getattribute____setattr__

1
2
3
4
5
6
7
8
9
class MyClass:
    def __init__(self, attrvalue):
        self.myattr = attrvalue
    def __getattribute__(self, attr):
        if attr =="myattr":
            #Getter for myattr
    def __setattr__(self, attr):
        if attr =="myattr":
            #Setter for myattr

请注意,__getattr____getattribute__是不同的。只有在找不到属性时才调用__getattr__