web-dev-qa-db-fra.com

Python class @property: utiliser setter mais esquiver getter?

Dans les classes python, @property est un décorateur sympa qui évite d'utiliser des fonctions setter et getter explicites. Cependant, cela coûte 2 à 5 fois les frais généraux d'une classe "classique"). Dans mon cas, c'est tout à fait correct dans le cas de la définition d'une propriété, où la surcharge est insignifiante par rapport au traitement qui doit être effectué lors de la définition.

Cependant, je n'ai besoin d'aucun traitement lors de l'obtention de la propriété. C'est toujours juste "return self.property". Existe-t-il une manière élégante d'utiliser le setter sans utiliser le getter, sans avoir besoin d'utiliser une variable interne différente?

Juste pour illustrer, la classe ci-dessous a la propriété "var" qui fait référence à la variable interne "_var". Il faut plus de temps pour appeler "var" que "_var" mais ce serait bien si les développeurs et les utilisateurs pouvaient simplement utiliser "var" sans avoir à garder une trace de "_var" aussi.

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'

Pour ceux qui sont intéressés, sur mon PC, appeler "var" prend 204 ns en moyenne tandis que appeler "_var" prend 44 ns en moyenne.

47
Jonas Lindeløv

N'utilisez pas de property dans ce cas. Un objet property est un descripteur de données, ce qui signifie que tout accès à instance.var Invoquera ce descripteur et Python ne cherchera jamais d'attribut sur l'instance elle-même .

Vous avez deux options: utilisez le crochet .__setattr__() ou créez un descripteur qui implémente uniquement .__set__.

Utilisation du crochet .__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)

Désormais, des recherches d'attributs normales sont utilisées lors de la lecture de la lecture .var Mais lors de l'attribution à .var De la méthode __setattr__ est invoqué à la place, vous permettant d'intercepter value et de l'ajuster au besoin.

Démo:

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

Un descripteur setter

Un descripteur de définition n'intercepterait que l'affectation de variable:

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

Notez comment nous devons attribuer à l'instance .__dict__ Attribut pour éviter d'appeler à nouveau le setter.

Démo:

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

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)

production:

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