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

2024-11-25 08:49:00
admin
原创
200
摘要:问题描述:我这样做是这样的:def set_property(property,value): def get_property(property): 或者object.property = value value = object.property 使用 getter 和 setter 的 Py...

问题描述:

我这样做是这样的:

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

或者

object.property = value  
value = object.property

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


解决方案 1:

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

解决方案 2:

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

“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__

第三,我们还有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 之间的整数(包括 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_...避免使用名为and 的函数get_...- 这就是属性的用途。

解决方案 3:

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

解决方案 4:

使用@property@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向客户端开发人员“隐藏”了属性,并且还对名称属性类型执行了检查。请注意,通过采用这种方法,即使在启动期间也会调用 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

解决方案 5:

这是一个老问题,但这个话题非常重要,而且始终是最新的。如果有人想超越简单的 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

解决方案 6:

属性非常有用,因为您可以将它们用于赋值,但也可以包含验证。您可以看到此代码,其中使用装饰器 @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/

解决方案 7:

您可以使用访问器/修改器(即@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 (或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

我很难猜测你的意图。一方面,你说ab都是公共的(它们的名称中没有前导下划线),所以理论上我应该被允许访问/改变(获取/设置)两者。但是你只为指定了一个显式变量a,这告诉我也许我不应该设置b。由于你提供了一个显式变量,我不确定缺少显式访问器(@property)是否意味着我不应该访问这两个变量中的任何一个,或者你只是在使用时很节俭@property

*例外情况是,当您明确希望使某些变量可访问或可变但不能同时实现这两种功能,或者您希望在访问或改变属性时执行一些额外的逻辑时。这是我个人使用@propertyand 的情况@attribute.setter(否则公共属性没有明确的访问器/改变器)。

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

PEP8,为继承而设计说:

对于简单的公共数据属性,最好只公开属性名称,而不要使用复杂的访问器/修改器方法。请记住,如果您发现简单的数据属性需要增加功能行为,Python 提供了一条通往未来增强的简单途径。在这种情况下,使用属性将功能实现隐藏在简单的数据属性访问语法后面。

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

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

这种方法的优点:

通过消除简单属性访问的显式 get 和 set 方法调用,可读性得到提高。允许计算延迟。被认为是维护类接口的 Pythonic 方式。在性能方面,当直接变量访问合理时,允许属性绕过需要琐碎的访问器方法。这也允许将来添加访问器方法而不会破坏接口。

和缺点:

在 Python 2 中必须继承object。可以像运算符重载一样隐藏副作用。可能会让子类感到困惑。

解决方案 8:

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

解决方案 9:

class ChangingPassword(object):
    def __init__(self, username, password):
        """use _ for change to read only type(protected)."""
        self.username = username
        self._password = password

    def username(self):
        return self.username

    @property
    def password(self):
        return self._password

    @password.setter
    def password(self, new_password: int):
        if isinstance(new_password, int):
            if self._password != new_password:
                self._password = new_password
            else:
                raise ValueError('Enter different value!')


user01 = ChangingPassword('Herment', 1321)
print(user01.password)
user01.password = 6301
print(user01.password)
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用