TypeError: ‘type’ object is niet itereerbaar – Itereren over objectinstanties

Ik werk aan een project en ik wil een van mijn lessen itereerbaar maken. Voor zover ik weet kan ik dat doen met metaclass.

Allereerst wil ik begrijpen hoe metaclass werkt. Daarom wil ik mijn eigen praktijkvoorbeeld presenteren waar ik een autoklas heb gemaakt. Dus hier zou ik mijn autoklasse-objecten itereerbaar willen maken, dan zou ik de namen ervan in de hoofdfunctie willen afdrukken.

Het codevoorbeeld is het volgende:

__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)

Maar helaas kreeg ik een TypeError:

TypeError: 'type' object is not iterable

Zou je zo vriendelijk willen zijn om me te vertellen waar ik de fout in mijn code maak? Tot nu toe heb ik soortgelijke probleemvragen via deze site en internet gecontroleerd, maar ik weet niet wat het probleem is. Ik gebruik python 3.4.
Bij voorbaat dank!


Antwoord 1, autoriteit 100%

Voor zover ik weet, werkt het itereerbaar maken van een klasseobject door een metaklasse te gebruiken prima:

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)

Resulteert in:

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

Hier is een voorbeeld waarbij ik de gegenereerde auto’s daadwerkelijk volg:

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)

Houd er rekening mee dat als je python3.x gebruikt, je het volgende doet om een metaklasse te gebruiken:

class Car(metaclass=IterableCar):
    ...

In plaats van:

class Car(object):
    __metaclass__ = IterableCar

wat waarschijnlijk het probleem verklaart dat je ervaart.


Antwoord 2, autoriteit 75%

Om instanties van de klasse die zijn gemaakt bij te houden, beginnen we met het toevoegen van een _cars-attribuut aan elke klasse die door de metaklasse is gemaakt. Dit zal een set zwakke referenties zijn, zodat de klasse zelf niet verhindert dat ongebruikte instanties worden verzameld.

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

Om de instanties toe te voegen, zullen we __call__overschrijven. In essentie voert u een code aan die u gewoonlijk in __new__of __init__zou plaatsen bij het definiëren van de klasse zelf.

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

en om de klasse acteerbaar te maken door herherend in zijn reeks gevallen,

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

Elke klasse met IterableCarzal automatisch zijn gevallen volgen.

class Car(metaclass=IterableCar):
    def __init__(self, name):
        self.name = name
car1 = Car('Mercedes')
car2 = Car('Toyota')
for cars in Car:
    print(cars.name)

Other episodes