Подтвердить что ты не робот

Класс Python @property: использовать setter, но избегать getter?

В классах python @property - хороший декоратор, который позволяет избежать использования явных функций setter и getter. Однако это связано со стоимостью накладных расходов в 2-5 раз выше, чем у "классической" функции класса. В моем случае это вполне нормально в случае установки свойства, где накладные расходы несущественны по сравнению с обработкой, которая должна быть выполнена при настройке.

Однако при получении свойства мне не нужна обработка. Это всегда просто "возвращение self.property". Есть ли элегантный способ использовать установщик, но не использовать геттер, не нужно использовать другую внутреннюю переменную?

Чтобы проиллюстрировать, класс ниже имеет свойство "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'

Для тех, кто интересуется, на моем компьютере вызов "var" занимает 204 нс в среднем, а вызов "_var" занимает в среднем 44 нс.

4b9b3361

Ответ 1

Не используйте property в этом случае. Объект property - это дескриптор данных, что означает, что любой доступ к instance.var будет вызывать этот дескриптор, и Python никогда не будет искать атрибут самого экземпляра.

У вас есть два варианта: используйте .__setattr__() или создайте дескриптор, который реализует только .__set__.

Использование .__setattr__() hook

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)

Теперь обычные чтения атрибутов используются при чтении .var, но при назначении .var вместо этого вызывается метод __setattr__, позволяя вам перехватить value и отрегулировать его по мере необходимости.

Демо:

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

Дескриптор сеттера

Дескриптор сеттера будет только перехватывать присвоение переменной:

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

Обратите внимание, как нам нужно назначить атрибуту .__dict__ экземпляра, чтобы предотвратить повторный вызов установщика.

Демо:

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

Ответ 2

property python docs: 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)

выход:

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