问题:使用getter和setter的pythonic方法是什么?
我这样做:
def set_property(property,value):
def get_property(property):
要么
object.property = value
value = object.property
我是Python的新手,因此我仍在探索语法,并且我希望对此提供一些建议。
回答 0
试试这个: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
回答 1
使用getter和setter的pythonic方法是什么?
“ Pythonic”方式不是使用“ getters”和“ setters”,而是使用简单的属性(如问题所展示的那样)并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__
。
第三,我们还具有setattr
和getattr
内置功能。
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用户所期望的。遵循最小惊奇规则,除非您有非常令人信服的相反理由,否则应尝试向用户提供他们期望的结果。
示范
例如,假设我们需要将对象的protected属性设置为0到100之间的整数(包括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
我不确定这在哪里有用,但是用例是您是否需要获取,设置和/或仅删除属性。最好坚持使用具有相同名称的语义上相同的属性。
结论
从简单的属性开始。
如果以后需要围绕设置,获取和删除的功能,则可以使用属性装饰器添加它。
避免将函数命名为set_...
和get_...
-这就是属性的作用。
回答 2
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
回答 3
使用@property
and @attribute.setter
帮助您不仅使用“ pythonic”方式,而且在创建对象和更改对象时都检查属性的有效性。
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
客户端开发人员的属性,并且还可以检查名称属性类型。请注意,即使在启动过程中也遵循此方法,将调用设置程序。所以:
p = Person(12)
将导致:
Exception: Invalid value for name
但:
>>>p = person('Mike')
>>>print(p.name)
Mike
>>>p.name = 'George'
>>>print(p.name)
George
>>>p.name = 2.3 # Causes an exception
回答 4
检查@property
装饰器。
回答 5
您可以使用存取器/更改器(即@attr.setter
和@property
),但最重要的是要保持一致!
如果您只是@property
用来访问属性,例如
class myClass:
def __init__(a):
self._a = a
@property
def a(self):
return self._a
使用它来访问every *属性!在不使用访问器的情况下使用以下属性访问某些属性@property
并使其他属性公开(即名称不带下划线)是不明智的做法,例如,不要这样做
class myClass:
def __init__(a, b):
self.a = a
self.b = b
@property
def a(self):
return self.a
请注意,self.b
即使它是公共的,这里也没有显式访问器。
与二传手(或mutators)类似,可以随意使用,@attribute.setter
但要保持一致!当你做例如
class myClass:
def __init__(a, b):
self.a = a
self.b = b
@a.setter
def a(self, value):
return self.a = value
我很难猜测你的意图。一方面,您是说a
和b
都是公开的(它们的名称中没有下划线),因此从理论上讲,应该允许我访问/更改(获取/设置)这两者。但是然后您只为a
它指定一个显式的mutator ,这告诉我也许我不能设置b
。由于您提供了一个显式的mutator,所以我不确定是否缺少显式的accessor(@property
)意味着我不能访问这些变量之一,或者您在使用时节俭@property
。
*exceptions情况是,当您明确希望使某些变量可访问或可变,但不能同时使二者可变,或者您希望在访问或更改属性时执行一些其他逻辑。这是我个人使用@property
和的时候@attribute.setter
(否则,没有用于公共属性的显式acessor / mutators)。
最后,PEP8和Google样式指南的建议:
PEP8,继承设计说:
对于简单的公共数据属性,最好仅公开属性名称,而不使用复杂的访问器/更改器方法。请记住,如果您发现简单的数据属性需要增强功能行为,那么Python为将来的增强提供了简便的途径。在那种情况下,使用属性将功能实现隐藏在简单的数据属性访问语法之后。
另一方面,根据Google样式指南Python语言规则/属性,建议:
使用新代码中的属性来访问或设置数据,而通常情况下,您应该使用简单,轻便的访问器或设置器方法。属性应使用
@property
装饰器创建。
这种方法的优点:
通过消除对简单属性访问的显式get和set方法调用,提高了可读性。允许计算是懒惰的。考虑使用Python方式维护类的接口。在性能方面,当直接变量访问合理时,允许属性绕过需要简单的访问器方法的情况。这也允许将来在不破坏接口的情况下添加访问器方法。
利弊:
必须
object
在Python 2中继承。可以隐藏副作用,就像运算符重载一样。对于子类可能会造成混淆。
回答 6
您可以使用魔术方法__getattribute__
和__setattr__
。
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__
仅在找不到属性时调用。