I am working on a project and I would like to make one of my classes iterable. To the best of my knowledge I can do that with using metaclass.
First of all I would like to understand how metaclass works. Therefore I would like to present my own practicing example where I made a Car class. So here I would like to make my Car class objects iterable then I would like to print the names of them in the main function.
The code example is the following:
__author__ = 'mirind4'
class IterableCar(type): def __iter__(self): return iter(self.__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.name)But unfortunately I got an TypeError:
TypeError: 'type' object is not iterableWould you be so kind as to tell me where I do the mistake in my code? So far I have checked similar problem-questions over this site and internet but I do not know what the problem is. I am using python 3.4. Thanks in advance!
93 Answers
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
rHere's 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 you're using python3.x, to use a metaclass you do:
class Car(metaclass=IterableCar): ...Rather than:
class Car(object): __metaclass__ = IterableCarwhich likely explains the problem that you're experiencing.
7To track instances of the class that are created, we'll 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, we'll 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 rvAnd 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) 3 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.