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

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

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

GreenAsJade
  • 14,459
  • 11
  • 63
  • 98

3 Answers3

5

If you really want that, you have to design it into your model - so that, for example, you have a registry of all created cars.

It is not a hard thing to do - I think the most obvious way is to customize the __new__ method in your class to keep that registry, and make the 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")

Another way, since this is class level things that should be kept when classes are created, could be dealt with in metaclasses. The good: it would be "pure" and you would not have special processing that happens when you instantiate the first object of that class as above. The bad: classes with different metaclasses are hard to combine - and if you have a class hierarchy in which some classes need this behavior, and some not - doing this at class level keeps everything interchangeable.

Actually, note that creating the superclass Base is an optional step. If you are only using it in one place in all your project, the logic could very well be inside the car class itself (as is in the answer by Narcise, bellow.)

If you don't want to do that, you can use the garbage collector (gc) module to find out all instances of your object and call the method on them. I don't think that would be a ice thing to do, as the gc itself is an implementation detail 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)]
jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • 1
    Why customize `__new__` and instead of `__init__`? – Steven Rumbalski May 21 '15 at 21:50
  • It could be done with `__init__` I guess - but since this will fiddle with class attributes only, and the derirved class might want to use `__init__` for other intents, I think it gets better separation. It is more a feeling than a "you should do that" thing. – jsbueno May 21 '15 at 21:58
  • Why create a base class, when the `vehicles` class looks as if it's meant to house vehicle types? – Zach Gates May 21 '15 at 22:07
  • Creating the baseclass is totally optional - and indeeed, I should mention that in the text - but it is a mechanism that will work for any class that inherit from it, and I doubt "car" will be the only place. – jsbueno May 21 '15 at 22:19
3
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
  • 1
    nice, simple answer - you can use "@classmethod" instead of "staticmethod" and use cls.child instead of Vehicule.child - that will make it work with sublasses as well, among other benefits. – jsbueno May 21 '15 at 22:21
1

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

You can then 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>]

Which isn't really helpful visually, but you can call the drive method on all the cars, like this:

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

Or in one line:

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

Which won't immediately drive the cars. You would need to call list on the map iterator.

Zach Gates
  • 4,045
  • 1
  • 27
  • 51
  • Note that `map` returns an iterator in Python 3 that won't immediately drive the cars, as opposed to a `list` in Python 2 that will drive immediately. – Navith May 22 '15 at 00:02