Python 类@property:使用 setter 但避开 getter?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17576009/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 08:34:22  来源:igfitidea点击:

Python class @property: use setter but evade getter?

pythonclasspropertiestiming

提问by Jonas Lindel?v

In python classes, the @property is a nice decorator that avoids using explicit setter and getter functions. However, it comes at a cost of an overhead 2-5 times that of a "classical" class function. In my case, this is quite OK in the case of setting a property, where the overhead is insignificant compared to the processing that needs to be done when setting.

在 python 类中,@property 是一个很好的装饰器,它避免使用显式的 setter 和 getter 函数。但是,它的开销是“经典”类函数的 2-5 倍。在我的情况下,这在设置属性的情况下非常好,与设置时需要完成的处理相比,开销微不足道。

However, I need no processing when getting the property. It is always just "return self.property". Is there an elegant way to use the setter but not using the getter, without needing to use a different internal variable?

但是,我在获得财产时不需要处理。它始终只是“返回 self.property”。有没有一种优雅的方法来使用 setter 但不使用 getter,而不需要使用不同的内部变量?

Just to illustrate, the class below has the property "var" which refers to the internal variable "_var". It takes longer to call "var" than "_var" but it would be nice if developers and users alike could just use "var" without having to keep track of "_var" too.

只是为了说明,下面的类具有属性“var”,它指的是内部变量“_var”。调用“var”比调用“_var”需要更长的时间,但如果开发人员和用户都可以只使用“var”而不必跟踪“_var”,那就太好了。

class MyClass(object):
  def __init__(self):
    self._var = None

  # the property "var". First the getter, then the setter
  @property
  def var(self):
    return self._var
  @var.setter
  def var(self, newValue):
    self._var = newValue
    #... and a lot of other stuff here

  # Use "var" a lot! How to avoid the overhead of the getter and not to call self._var!
  def useAttribute(self):
    for i in xrange(100000):
      self.var == 'something'

For those interested, on my pc calling "var" takes 204 ns on average while calling "_var" takes 44 ns on average.

对于那些感兴趣的人,在我的电脑上调用“var”平均需要 204 ns,而调用“_var”平均需要 44 ns。

采纳答案by Martijn Pieters

Don't use a propertyin this case. A propertyobject is a data descriptor, which means that any access to instance.varwill invoke that descriptor and Python will never look for an attribute on the instance itself.

property在这种情况下不要使用 a 。一个property对象是一个数据描述符,这意味着任何访问instance.var将调用该描述符和Python将永远不会寻找在实例本身的属性。

You have two options: use the .__setattr__()hook or build a descriptor that only implements .__set__.

您有两个选择:使用.__setattr__()钩子或构建仅实现.__set__.

Using the .__setattr__()hook

使用.__setattr__()钩子

class MyClass(object):
    var = 'foo'

    def __setattr__(self, name, value):
        if name == 'var':
            print "Setting var!"
            # do something with `value` here, like you would in a
            # setter.
            value = 'Set to ' + value
        super(MyClass, self).__setattr__(name, value)

Now normal attribute lookups are used when reading.varbut when assigning to .varthe __setattr__method is invoked instead, letting you intercept valueand adjust it as needed.

现在,当正常的属性查询用来阅读.var,但分配给当.var__setattr__方法被调用来代替,让你拦截value,并根据需要进行调整。

Demo:

演示:

>>> mc = MyClass()
>>> mc.var
'foo'
>>> mc.var = 'bar'
Setting var!
>>> mc.var
'Set to bar'

A setter descriptor

一个 setter 描述符

A setter descriptor would only intercept variable assignment:

setter 描述符只会拦截变量赋值:

class SetterProperty(object):
    def __init__(self, func, doc=None):
        self.func = func
        self.__doc__ = doc if doc is not None else func.__doc__
    def __set__(self, obj, value):
        return self.func(obj, value)

class Foo(object):
    @SetterProperty
    def var(self, value):
        print 'Setting var!'
        self.__dict__['var'] = value

Note how we need to assign to the instance .__dict__attribute to prevent invoking the setter again.

请注意我们需要如何分配实例.__dict__属性以防止再次调用 setter。

Demo:

演示:

>>> f = Foo()
>>> f.var = 'spam'
Setting var!
>>> f.var = 'ham'
Setting var!
>>> f.var
'ham'
>>> f.var = 'biggles'
Setting var!
>>> f.var
'biggles'

回答by WeizhongTu

propertypython docs: https://docs.python.org/2/howto/descriptor.html#properties

propertypython 文档:https: //docs.python.org/2/howto/descriptor.html#properties

class MyClass(object):
    def __init__(self):
        self._var = None

    # only setter
    def var(self, newValue):
        self._var = newValue

    var = property(None, var)


c = MyClass()
c.var = 3
print ('ok')
print (c.var)

output:

输出:

ok
Traceback (most recent call last):
  File "Untitled.py", line 15, in <module>
    print c.var
AttributeError: unreadable attribute

回答by Nuno André

The accepted answer's setter descriptor would be probably more convenient if it set the property by itself:

如果它自己设置属性,则接受的答案的 setter 描述符可能会更方便:

A setter descriptor (alt.)

一个 setter 描述符 (alt.)

class setter:
    def __init__(self, func, doc=None):
        self.func = func
        self.__doc__ = doc or func.__doc__

    def __set__(self, obj, value):
        obj.__dict__[self.func.__name__] = self.func(obj, value)

class Foo:
    @setter
    def var(self, value):
        print('Setting var!')

        # validations and/or operations on received value
        if not isinstance(value, str):
            raise ValueError('`var` must be a string')
        value = value.capitalize()

        # returns property value
        return value

Demo:

演示:

>>> f = Foo()
>>> f.var = 'spam'
Setting var!
>>> f.var = 'ham'
Setting var!
>>> f.var
'Ham'
>>> f.var = 'biggles'
Setting var!
>>> f.var
'Biggles'
>>> f.var = 3
ValueError: `var` must be a string

回答by Lore

The @WeizhongTu answer

@WeizhongTu 的回答

class MyClass(object):
    def __init__(self):
        self._var = None

    # only setter
    def var(self, newValue):
        self._var = newValue

    var = property(None, var)


c = MyClass()
c.var = 3
print ('ok')
print (c.var)

Is fine, except from the fact that is making the variable ungettable...

很好,除了使变量无法获取的事实......

A similar solution but preserving getteris with

一个类似的解决方案,但保留吸气剂

var = property(lambda self: self._var, var)

instead of

代替

var = property(None, var)