简体   繁体   English

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

[英]Python class @property: use setter but evade getter?

In python classes, the @property is a nice decorator that avoids using explicit setter and getter functions.在 python 类中,@property 是一个很好的装饰器,它避免使用显式的 setter 和 getter 函数。 However, it comes at a cost of an overhead 2-5 times that of a "classical" class function.但是,它的开销是“经典”类函数的 2-5 倍。 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.在我的情况下,这在设置属性的情况下非常好,与设置时需要完成的处理相比,开销微不足道。

However, I need no processing when getting the property.但是,我在获得财产时不需要处理。 It is always just "return self.property".它始终只是“返回 self.property”。 Is there an elegant way to use the setter but not using the getter, without needing to use a different internal variable?有没有一种优雅的方法来使用 setter 但不使用 getter,而不需要使用不同的内部变量?

Just to illustrate, the class below has the property "var" which refers to the internal variable "_var".只是为了说明,下面的类具有属性“var”,它指的是内部变量“_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”,那就太好了。

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。

Don't use a property in this case.在这种情况下不要使用property A property object is a data descriptor, which means that any access to instance.var will invoke that descriptor and Python will never look for an attribute on the instance itself. 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 .var but when assigning to .var the __setattr__ method is invoked instead, letting you intercept value and 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'

property python docs: https://docs.python.org/2/howto/descriptor.html#properties property python 文档: 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

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 getter is with一个类似的解决方案,但保留吸气剂

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

instead of代替

var = property(None, var)

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM