Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

classmethod property TypeError: 'property' object is not iterable

Tags:

python

Running this code:

import weakref

class A(object):
    _instances = []
    def __init__(self):
        self._instances.append(weakref.ref(self))

    @property
    @classmethod
    def instances(cls):
        for inst_ref in cls._instances:
            inst = inst_ref()
            if inst is not None:
                yield inst

foo = A()
bar = A()
for inst in A.instances:
    print inst

I get this error:

Traceback (most recent call last):
  File "test.py", line 18, in <module>
    for inst in A.instances:
TypeError: 'property' object is not iterable

I can't figure out how having a class method behave like a property (no parentheses).

  • Can anyone explain me why I get this error?
  • Can anyone explain me how I could have a class method behaving like a property?
like image 962
Narann Avatar asked May 01 '15 20:05

Narann


2 Answers

Here is one way to use descriptors with a class:

import weakref

class classproperty(object):
    def __init__(self, fget):
        self.fget = fget
    def __get__(self, owner_self, owner_cls):
        return self.fget(owner_cls)

class A(object):
    _instances = []
    def __init__(self):
        self._instances.append(weakref.ref(self))

    @classproperty
    def instances(cls):
        for inst_ref in cls._instances:
            inst = inst_ref()
            if inst is not None:
                yield inst

foo = A()
bar = A()
for inst in A.instances:
    print inst

References:

  • https://docs.python.org/2/reference/datamodel.html#invoking-descriptors
  • https://stackoverflow.com/a/13624858/8747
like image 65
Robᵩ Avatar answered Sep 28 '22 04:09

Robᵩ


Properties always apply to instances, not classes.

The way to do this would be to define a metaclass that defines the property on its own instance method, since a class is an instance of its metaclass:

class AMeta(type):
    def __init__(self,name,bases,dict):
        self._instances = []

    @property
    def instances(self):
        for inst_ref in self._instances:
             inst = inst_ref()
             if inst is not None:
                 yield inst

class A(object):
     __metaclass__ = AMeta

     def __init__(self):
         self._instances.append(weakref.ref(self))

This now works as expected:

>>> foo=A()
>>> bar = A()

>>> for inst in A.instances:
...     print inst
<__main__.A object at 0x1065d7290>
<__main__.A object at 0x1065d7990>
like image 37
Daniel Roseman Avatar answered Sep 28 '22 02:09

Daniel Roseman