python – TypeError: type object is not iterable – Iterating over object instances

python – TypeError: type object is not iterable – Iterating over object instances

As far as I can tell, making a class object iterable by using a metaclass works just fine:

from __future__ import print_function

class IterableCar(type):
    def __iter__(cls):
        return iter(cls.__name__)

class Car(object):
    __metaclass__ = IterableCar

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


if __name__==__main__:

    car1 = Car(Mercedes)
    car2 = Car(Toyota)
    for cars in Car:
        print (cars)

Results in:

mgilson$ python ~/sandbox/test.py 
C
a
r

Heres an example where I actually track the cars generated:

from __future__ import print_function
import weakref

class IterableCar(type):

    _cars = weakref.WeakSet()

    def __iter__(cls):
        return iter(cls._cars)

    def add_car(cls, car):
        cls._cars.add(car)


class Car(object):
    __metaclass__ = IterableCar

    def __init__(self, name):
        self.__class__.add_car(self)
        self.name = name


if __name__==__main__:

    car1 = Car(Mercedes)
    car2 = Car(Toyota)
    for cars in Car:
        print (cars.name)

Note that if youre using python3.x, to use a metaclass you do:

class Car(metaclass=IterableCar):
    ...

Rather than:

class Car(object):
    __metaclass__ = IterableCar

which likely explains the problem that youre experiencing.

To track instances of the class that are created, well start by adding a _cars attribute to each the class created by the metaclass. This will be set of weak references, so that the class itself does not prevent unused instances from being garbage-collected.

class IterableCar(type):
    def __new__(meta, name, bases, attrs):
        attrs[_cars] = weaker.WeakSet()
        return type.__new__(meta, name, bases, attrs)

To add the instances, well override __call__. Essentially, this is where you put code that you would ordinarily put in __new__ or __init__ when defining the class itself.

    def __call__(cls, *args, **kwargs):
        rv = type.__call__(cls, *args, **kwargs)
        cls._cars.add(rv)
        return rv

And to make the class iterable by iterating over its set of instances,

   def __iter__(self):
       return iter(self._cars)

Any class using IterableCar will automatically track its instances.

class Car(metaclass=IterableCar):
    def __init__(self, name):
        self.name = name

car1 = Car(Mercedes)
car2 = Car(Toyota)
for cars in Car:
    print(cars.name)

python – TypeError: type object is not iterable – Iterating over object instances

I had this error appear when a decorator was expecting a Tuple and I passed in a single element without a trailing comma.

@api_view([GET])
@authentication_classes((CustomAuthentication,))
@permission_classes((AdminPermission))
def order_attached_documents_from_order_uuid(request):
    # ...
    return Response(status=status.HTTP_200_OK)

After I changed @permission_classes((AdminPermission)) to @permission_classes((AdminPermission,)) everything started working just fine.

Leave a Reply

Your email address will not be published.