En este código de ejemplo, me gustaría determinar si x es una instancia de TestProperty:

class TestProperty(object):
    def __init__(self, name):
        self._name = name

    def __get__(self, instance, cls):
        return getattr(instance, self._name)

    def __set_(self, instance, value):
        setattr(instance, self._name, value)

class Test(object):
    x = TestProperty("x")

print isinstance(Test.x, TestProperty)

Sin embargo, obtengo la siguiente excepción:

Traceback (most recent call last):
  File "/home/zenoss/testproperties.py", line 14, in <module>
    print isinstance(Test.x, TestProperty)
  File "/home/zenoss/testproperties.py", line 6, in __get__
    return getattr(instance, self._name)
AttributeError: 'NoneType' object has no attribute 'x'

¿Hay alguna forma de saber si un atributo es una instancia de una clase cuando es un descriptor?

1
Ben 8 mar. 2017 a las 19:19

2 respuestas

La mejor respuesta

Con el __get__ actual, Test.x causa el AttributeError porque cuando el código que accede al descriptor usando class, instance se pasa None; (=> getattr(None, 'x') => None.x)

Debe modificar __get__ para manejar este caso:

>>> class TestProperty(object):
...     def __init__(self, name):
...         self._name = name
...     def __get__(self, instance, cls):
...         if instance is None:  # To handle access through class, not instance
...             return self       # returns the TestProperty instance itself.
...         return getattr(instance, self._name)
...     def __set_(self, instance, value):
...         setattr(instance, self._name, value)
... 
>>> class Test(object):
...     x = TestProperty("x")
... 
>>> isinstance(Test.x, TestProperty)
True

Por cierto, como sabrá, con x = TestProperty("x"), acceder al atributo x a través de una instancia provocará otra excepción, porque llamará al __get__ (-> getattr (..) -> __get__ -> getattr (..) -> ...) de forma recursiva hasta que la pila se desborde.

3
falsetru 8 mar. 2017 a las 16:28

La mejor manera de implementar una propiedad es con el decorador @property:

class TestProperty(object):

    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        """Getter for '_name'."""
        return self._name

    @name.setter
    def name(self, value):
        """Setter for '_name'."""
        self._name = value

class Test(object):
    x = TestProperty("x")

print(isinstance(Test.x, TestProperty))

Devuelve True cuando lo ejecuto. Consulte la documentación de @property en https: //docs.python. org / 3 / library / functions.html # propiedad.

0
Isaac Saffold 8 mar. 2017 a las 16:48