Python的@setter装饰器的含义是什么?

huangapple go评论65阅读模式
英文:

Meaning of Python's @setter decorator

问题

在网站https://stackoverflow.com/questions/17330160/how-does-the-property-decorator-work-in-python中,你可以找到许多关于@property装饰器含义的答案,但没有关于相应的@propertyname.setter装饰器的答案。

我的问题是以下的。在下面的代码中,是否可能用单个语句替换@x.setter装饰器?

class D:
    def __init__(self):
        self._x = 'Hi'

    def x(self):
        return self._x
    x = property(x)

    @x.setter #删除这个装饰器
    def x(self, value):
        self._x = value
    # 你的代码,一行!
英文:

In the website https://stackoverflow.com/questions/17330160/how-does-the-property-decorator-work-in-python you can find many answers about the meaning of the @property decorator, but no answer about the corresponding @propertyname.setter decorator.

My question is the following. In the code below, is it possible to replace the @x.setter decorator with a single statement?

class D:
    def __init__(self):
        self._x = 'Hi'

    def x(self):
        return self._x
    x = property(x)

    @x.setter #delete this decorator
    def x(self, value):
       self._x = value
    # Your code, one line!

答案1

得分: 2

没有办法用一行代码完全替代你的setter方法的装饰符语法。这是因为在这种特定情况下,手动应用装饰符与@decorator语法的应用方式存在细微差别。

这个差别在于,使用@decorator语法时,要装饰的函数在传递给装饰器之前从未与名称绑定。在你的代码中,x这个名称已经在我们的命名空间中使用,用于装饰getter函数的属性对象。如果我们暂时将x绑定到未经装饰的setter函数,就会失去对该对象的访问权,而我们需要它,因为我们想应用的装饰器是它的一个属性!

你可以通过为setter函数指定一个不同的名称来解决这个问题:

class D:
    def __init__(self):
        self._x = 'Hi'

    def x(self):
        return self._x
    x = property(x)   # 这是先前的x值,我们最终会覆盖它

    def x_setter(self, value):   # 更改此函数的名称,以免立即覆盖x
       self._x = value
    x = x.setter(x_setter)       # 然后手动应用装饰器,覆盖x

    # 这里我们可能想要 `del x_setter`,以避免在命名空间中产生混乱

正如我的注释所示,先前的x值最终会被我们新的包装了getter和setter方法的属性所覆盖。但我们不能太早这样做,否则我们将无法使用旧版本的x访问x.setter

手动调用property同时传递getter和setter方法,以及可能的deleter方法,是一种更自然的方式:

class D:
    def __init__(self):
        self._x = 'Hi'

    def x_getter(self):
        return self._x

    def x_setter(self, value):
       self._x = value

    x = property(x_getter, x_setter)   # 一次性包装getter和setter
英文:

There's no way to replace the decorator syntax for your setter method with exactly one line. That's because in this specific situation, the little detail of how applying the decorator manually differs from @decorator syntax matters.

That difference is that with @decorator syntax, the function to be decorated is never bound to a name before it's passed to the decorator. In your code, the x name is already in use in our namespace, for the property object decorating the getter function. If we temporarily bind x to the undecorated setter function, we'll lose access to that object, and we need it, since the decorator we want to apply is one of its attributes!

You can work around this by giving the setter function a different name:

class D:
    def __init__(self):
        self._x = 'Hi'

    def x(self):
        return self._x
    x = property(x)   # this is the previous x value, which we will eventually overwrite
    
    def x_setter(self, value):   # rename this function, so we don't clobber x quite yet
       self._x = value
    x = x.setter(x_setter)       # then apply the decorator manually, overwriting x

    # we might want to `del x_setter` here, to avoid cluttering the namespace

As my comments show, the previous x value does eventually get overwritten, by our new property that wraps both the getter and setter methods. But we can't do it too soon, or we won't be able to access x.setter using the old version of x.

A more natural way to call property manually with both getter and setter methods is to pass both of them (and maybe a deleter too) in a single property call:

class D:
    def __init__(self):
        self._x = 'Hi'

    def x_getter(self):
        return self._x

    def x_setter(self, value):
       self._x = value

    x = property(x_getter, x_setter)   # wrap both getter and setter in one call

答案2

得分: 1

这里是代码部分的翻译:

class Test:
    x = property(
        lambda self: getattr(self, "_x"),  # 获取器
        lambda self, value: setattr(
            self, "_x", f"{value} 来自设置器"
        ),  # 设置器
    )

    def __init__(self):
        print("init 被调用")
        self.x = "你好"


test = Test()
test.x = "你好吗"
print(test.x)

输出:

init 被调用
你好吗 来自设置器

property 类包含了三个方法:获取器、设置器和删除器。根据名称可以猜出它们的功能。你可以使用 __init__ 方法来初始化你需要的方法,或者稍后显式调用这些方法并设置函数。

我可以知道你为什么想要这样做吗?

英文:

Here,

class Test:
    x = property(
        lambda self: getattr(self, "_x"),  # getter
        lambda self, value: setattr(
            self, "_x", f"{value} From Setter"
        ),  # setter
    )

    def __init__(self):
        print("init called")
        self.x = "Hi"


test = Test()
test.x = "Hello"
print(test.x)

Output:

init called
Hello From Setter

The property class has three methods within it getter, setter, and deleter. You can guess by the name what they do. You can either use a init method to initialize whatever method you need or you can explicitly call the methods later and set the functions.

May I know why you wanna do that?

huangapple
  • 本文由 发表于 2023年5月28日 04:05:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/76348824.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定