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 iterable
Would 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!
To create an object/class as an iterator you have to implement the methods __iter__() and __next__() to your object. As you have learned in the Python Classes/Objects chapter, all classes have a function called __init__() , which allows you to do some initializing when the object is being created.
The JavaScript exception "is not iterable" occurs when the value which is given as the right-hand side of for...of , as argument of a function such as Promise. all or TypedArray. from , or as the right-hand side of an array destructuring assignment, is not an iterable object.
Conclusion # The Python "TypeError: 'float' object is not iterable" occurs when we try to iterate over a float or pass a float to a built-in function like, list() or tuple() . To solve the error, use the range() built-in function to iterate over a range, e.g. for i in range(int(3.0)): .
The Python "TypeError: argument of type 'int' is not iterable" occurs when we use the membership test operators (in and not in) with an integer value. To solve the error, correct the assignment or convert the int to a string, e.g. str(my_int) .
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
Here'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__ = IterableCar
which likely explains the problem that you're experiencing.
To 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 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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With