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

我这样做:

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

object.property = value
value = object.property

我是Python的新手,所以我仍然在探索语法,我想要一些这样做的建议。

487898 次浏览
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

试试这个: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

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

“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

(每次使用装饰器都会复制并更新前面的属性对象,因此请注意,您应该为每个set、get和delete函数/方法使用相同的名称。)

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

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用户所期望的。遵循最小惊喜原则,你应该尽量满足用户的期望,除非你有非常令人信服的相反理由。

示范

例如,我们需要我们的对象的protected属性是一个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

我不确定这在什么情况下有用,但用例是如果您想要一个get、set和/或delete-only属性。可能最好是使用语义上相同的属性和相同的名称。

结论

从简单的属性开始。

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

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

使用@property@attribute.setter不仅可以帮助您使用“python”方式,还可以在创建对象和修改对象时检查属性的有效性。

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属性,并对name属性类型进行了检查。请注意,即使在初始化过程中也会调用setter。所以:

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

你可以使用魔法方法__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__仅在未找到属性时调用。

您可以使用访问器/突变器(即@attr.setter@property),也可以不使用,但最重要的是保持一致!

如果您使用@property来简单地访问一个属性,例如

class myClass:
def __init__(a):
self._a = a


@property
def a(self):
return self._a


使用它来访问每一个*属性!使用@property访问某些属性,而使用公共(即名称不带下划线)而不使用访问器,例如不要做,这将是一个糟糕的做法

class myClass:
def __init__(a, b):
self.a = a
self.b = b


@property
def a(self):
return self.a


注意,self.b在这里没有显式访问器,尽管它是公共的。

setter(或调整器)类似,可以随意使用@attribute.setter,但可以使用是一致的!

class myClass:
def __init__(a, b):
self.a = a
self.b = b


@a.setter
def a(self, value):
return self.a = value

我很难猜出你的意图。一方面,您说ab都是公共的(在它们的名称中没有前导下划线),因此理论上应该允许我访问/更改(获取/设置)这两者。但随后您仅为a指定了显式突变子,这告诉我,也许我不应该能够设置b。因为你已经提供了一个显式的突变,我不确定是否缺乏显式访问器(@property)意味着我不应该能够访问这些变量中的任何一个,或者你只是在使用@property时很节俭。

*例外情况是当你显式地想让一些变量可访问或可变但不是两者都有,或者你想在访问或改变一个属性时执行一些额外的逻辑。这是当我个人使用@property@attribute.setter时(否则没有显式的公共属性访问器/突变器)。

最后,PEP8和谷歌风格指南建议:

PEP8, 为传承而设计说:

对于简单的公共数据属性,它是最好只公开属性名,而不使用复杂的访问器/突变器方法。请记住,如果您发现一个简单的数据属性需要增长函数行为,Python为将来的增强提供了一个简单的途径。在这种情况下,使用属性将函数实现隐藏在简单的数据属性访问语法后面。

另一方面,根据谷歌风格指南Python语言规则/属性的建议是:

在新代码中使用属性来访问或设置数据,而通常使用简单、轻量级的访问器或setter方法。属性应该使用@property装饰器创建。

这种方法的优点:

通过消除用于简单属性访问的显式get和set方法调用,提高了可读性。允许延迟计算。考虑了维护类接口的python方式。就性能而言,当直接变量访问是合理的时,允许属性绕过了需要简单的访问器方法。这也允许将来在不破坏接口的情况下添加访问器方法。

优缺点:

必须从Python 2中的object继承。可以隐藏像操作符重载这样的副作用。子类可能会让人困惑。

这是一个老问题,但这个话题非常重要,而且一直是最新的。如果有人想超越简单的getter /setter,我已经写了一篇关于python中超级属性的文章,支持插槽,可观察性和简化的样板代码。

from objects import properties, self_properties




class Car:
with properties(locals(), 'meta') as meta:


@meta.prop(read_only=True)
def brand(self) -> str:
"""Brand"""


@meta.prop(read_only=True)
def max_speed(self) -> float:
"""Maximum car speed"""


@meta.prop(listener='_on_acceleration')
def speed(self) -> float:
"""Speed of the car"""
return 0  # Default stopped


@meta.prop(listener='_on_off_listener')
def on(self) -> bool:
"""Engine state"""
return False


def __init__(self, brand: str, max_speed: float = 200):
self_properties(self, locals())


def _on_off_listener(self, prop, old, on):
if on:
print(f"{self.brand} Turned on, Runnnnnn")
else:
self._speed = 0
print(f"{self.brand} Turned off.")


def _on_acceleration(self, prop, old, speed):
if self.on:
if speed > self.max_speed:
print(f"{self.brand} {speed}km/h Bang! Engine exploded!")
self.on = False
else:
print(f"{self.brand} New speed: {speed}km/h")
else:
print(f"{self.brand} Car is off, no speed change")

这个类可以这样使用:

mycar = Car('Ford')


# Car is turned off
for speed in range(0, 300, 50):
mycar.speed = speed


# Car is turned on
mycar.on = True
for speed in range(0, 350, 50):
mycar.speed = speed

这段代码将产生以下输出:

Ford Car is off, no speed change
Ford Car is off, no speed change
Ford Car is off, no speed change
Ford Car is off, no speed change
Ford Car is off, no speed change
Ford Car is off, no speed change
Ford Turned on, Runnnnnn
Ford New speed: 0km/h
Ford New speed: 50km/h
Ford New speed: 100km/h
Ford New speed: 150km/h
Ford New speed: 200km/h
Ford 250km/h Bang! Engine exploded!
Ford Turned off.
Ford Car is off, no speed change

更多关于如何和为什么的信息:https://mnesarco.github.io/blog/2020/07/23/python-metaprogramming-properties-on-steroids

属性非常有用,因为你可以在赋值时使用它们,但也可以包括验证。你可以在使用装饰器@property和@<property_name>的地方看到这段代码。Setter来创建方法:

# Python program displaying the use of @property
class AgeSet:
def __init__(self):
self._age = 0


# using property decorator a getter function
@property
def age(self):
print("getter method called")
return self._age


# a setter function
@age.setter
def age(self, a):
if(a < 18):
raise ValueError("Sorry your age is below eligibility criteria")
print("setter method called")
self._age = a


pkj = AgeSet()


pkj.age = int(input("set the age using setter: "))


print(pkj.age)

我在这篇文章中也写了更多关于这个的细节:https://pythonhowtoprogram.com/how-to-create-getter-setter-class-properties-in-python-3/