问题
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!
回答1:
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.
回答2:
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)
来源:https://stackoverflow.com/questions/32362148/typeerror-type-object-is-not-iterable-iterating-over-object-instances