Method call on all object instances

class vehicles:
    class car:
        def drive():
            pass

car1 = car()
car2 = car()
car3 = car()

      

Is there a way that I can call the drive function on all cars in one call, without explicitly naming each one.

+3


source to share


3 answers


If you really want it, you have to create it in your model, so for example you have a register of all cars created.

It's not hard to do - I think the most obvious way is to set up __new__

in your class to keep this registry and make calls:

class Base:
    def __new__(cls, *args, **kw):
        self = super.__new__(*args, **kw)
        if not hasattr(cls, _instances):
            cls._instances = []
        cls._instances.append(self)
        return self

    @classmethod
    def call_all(cls, method_name, *args, **kw):
        results = []
        for instance in cls._instances:
            results.append(getattr(instance, method_name)(*args, **kw))
        return results

    def __del__(self):
        self.__class__._instances.remoce(self)

class vehicles:
    class car(Base):

        def drive():
            pass

car1 = car()
car2 = car()
car3 = car()

car.call_all("drive")

      

Alternatively, since these are class things that need to be preserved when classes are created, they can be viewed in metaclasses. The good news is that it would be "clean" and you wouldn't have the special handling that occurs when you instantiate the first object of this class as above. Bad: Classes with different metaclasses are difficult to combine - and if you have a class hierarchy where some classes require this behavior, and some don't do it at the class level, everything is interchangeable.

Actually, note that creating a superclass Base

is an optional step. If you only use it in one place in your entire project, the logic could very well be inside the class itself car

(as in Narcise's answer, below).



If you don't want to do this, you can use the garbage collector ( gc

) module to find out all instances of your object and call a method on them. I don't think it would be an icy thing, since the very gc

implementation details are for cPython.

import gc

...
for obj in gc.get_referrers(car):
     if not isinstance(obj, car): continue
     obj.drive()

      

or in an ugly oneliner:

[getattr(obj, "drive")() for obj in gc.get_referrers(car) if isinstance(obj, car)]

      

+5


source


class Vehicule():

      pos, child = 0, []

      def __init__(self):
          self.__class__.child.append(self)
          self.pos = self.__class__.pos
          self.__class__.pos +=1

      def cond(self):
          print self.pos

      @classmethod
      def drive(cls):
          for v in cls.child:
              v.cond()

      

Testing:



t = Vehicule()
c = Vehicule()
g = Vehicule()
Vehicule.drive()

>>0
>>1
>>2

      

+3


source


To answer your question, yes, there is a way. You can use a class variable to keep track of instances.

class vehicles:

    working_vehicles = [] # this is a class variable

    class car:
        def __init__(self):
            vehicles.working_vehicles.append(self) # adds itself to working_vehicles

        def drive(self):
            pass

      

Then you can create instances and access them from vehicles.working_vehicles

.

Demo:

First create the cars:

>>> car1 = vehicles.car()
>>> car2 = vehicles.car()
>>> car3 = vehicles.car()

      

Then access them from vehicles.working_vehicles

.

>>> print(vehicles.working_vehicles)
[<__main__.vehicles.car object at 0x022DF6F0>, <__main__.vehicles.car object at 0x01E97ED0>, <__main__.vehicles.car object at 0x01EAA6B0>]

      

It's not very useful visually, but you can call the method drive

on all cars, for example:

>>> for car in vehicles.working_vehicles:
...     car.drive()

      

Or in one line:

>>> map(lambda car: car.drive(), vehicles.working_vehicles)

      

Which will not immediately drive cars. You will need to call list

on the iterator map

.

+1


source







All Articles