1

I am not sure if the title of the question is correct.

I am trying to achieve the following.


class A():
   def __init__(self, name):
       self.name = name
    
   def foo1(self):
       print(1, self.name)

   def foo2(self):
       print(2, self.name)

a1 = A('a1')
a1.foo1() # print '1, a1'

a2 = A('a2')
b = SomeFoo(a1, a2)  # or, SomeFoo([a1, a2])
b.foo1()  # calls a1.foo1(), a2.foo2() 

Similarly,

b.foo2() should call foo2 of both a1 and a2. I guess np.vectorize is something similar.

What would this potential SomeFoo (class or function) be called?

And more importantly, what is a good way to write SomeFoo?

Update

Replaced print and printB with foo1 and foo2.

Dilawar
  • 5,438
  • 9
  • 45
  • 58
  • Past answers to similar questions (such as [Idiomatic way to call method on all objects in a list of objects Python 3](https://stackoverflow.com/questions/51519696/idiomatic-way-to-call-method-on-all-objects-in-a-list-of-objects-python-3)) generally say just to use a for-loop. See also [How to call same method for a list of objects?](https://stackoverflow.com/questions/2682012/how-to-call-same-method-for-a-list-of-objects). – Dennis Sparrow Jun 30 '20 at 01:52
  • Thanks @DennisSparrow . I want `someFoo(a1, a2, a3, a4).bar()` calling `a1.bar(), ..., a4.bar()`. I was hoping that `someFoo` needs not declare `bar`. Not sure if it is possible without hacking into class attributes. – Dilawar Jun 30 '20 at 01:58
  • Dilawar, actually, I was wondering if you were looking for something more general than the answers you have been getting, so it's good that you clarified it. My guess is that it can't be done exactly as you specified, but some variant like `b.callMethod("foo1")` might be possible. – Dennis Sparrow Jun 30 '20 at 03:02

4 Answers4

1

It is a little unclear what you are trying to achieve, but I can guess I suppose.

Each call of A() creates a single instance. What you are trying to do requires that each instance knows about each other -- ie, they have attributes common to the class.

Example:

class A():
    members={}
    def __init__(self, name, func=lambda e: print(self.name)):
        self.name = name
        self.members[name]=func

    def __del__(self):
        del self.members[self.name] 

    def do_all(self):
        for k, func in self.members.items():
            print(k)
            func() 
        
a=A('a1', lambda: print('I am the great and powerful OZ'))  
b=A('b1', lambda: print('paCHOO!')) 

b.do_all()

In practice:

a=A('a1', lambda: print('I am the great and powerful OZ'))  
b=A('b1', lambda: print('paCHOO!')) 

b.do_all()

Prints:

a1
I am the great and powerful OZ
b1
paCHOO!
dawg
  • 98,345
  • 23
  • 131
  • 206
1

In response to your desire for generality, consider this class:

class SomeFoo:
    def __init__(self, *args):
        self.objects = [*args]

    def callMethod(self, methodName):
        for object in self.objects:
            if hasattr(object, methodName):
                method = getattr(object, methodName)
                if callable(method):
                    method()

(I don't understand why you asked what it would be called. You can name it whatever you like. I called it SomeFoo to match your question.)

You would use this class like this:

b = SomeFoo(a1, a2)  
b.callMethod("foo1")
b.callMethod("foo2")

This design doesn't require that SomeFoo know in advance the name of any function to be called, and it doesn't even require that all the parameter objects be of the same class. If one of the objects does not have the specified method, it will be silently and harmlessly bypassed.

As you guessed, it requires "hacking into class attributes."

Of course, this could blow up if, for example, an object had a method with the right name that required a parameter. This could be checked with some additional code.

Edit: Since posting the answer above, I have found that it is possible to support the b.foo1() calling interface specified in the question. I can post the code if anyone cares.

Dennis Sparrow
  • 938
  • 6
  • 13
0

The syntax you are using:

b = someFoo(a1, a2)
b.print()

makes it look like you want to use a class, but based on the functionality you are describing I think the simplest way to implement this is with a function. It saves you having to write a class and you can initiate the print on one line instead of requiring two.

I have added a type hint (List[A]) to make the function clearer, but this is not necessary and not supported before python 3.

from typing import List

def someFoo(classAs: List[A]) -> None:
    for a in classAs:
        a.printB()

a1 = A('a1')
a2 = A('a2')
someFoo([a1, a2])
wjw
  • 209
  • 2
  • 6
-1
class b:
    def __init__(self, *args):
        self.thelist = *args
    def print(self):
        [print(i) for i in self.thelist]

No?

Igor Rivin
  • 4,632
  • 2
  • 23
  • 35
  • 1
    No, it's supposed to do `i.print()`, not `print(i)`. Otherwise OK. – Dennis Sparrow Jun 30 '20 at 01:31
  • 2
    [Don't use comprehensions for side effects](https://stackoverflow.com/q/5753597/4518341). Use a plain for-loop instead. – wjandrea Jun 30 '20 at 01:35
  • Do: `'\n'.join([str(i) for i in self.thelist])` instead of `[print(i) for i in self.thelist]` – dawg Jun 30 '20 at 01:41
  • What do you mean by "no"? Could you add some explanation to your answer such that others can learn from it? – Nico Haase Jun 30 '20 at 09:28
  • Hello! While this code may solve the question, [including an explanation](https://meta.stackexchange.com/q/114762) of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please [edit] your answer to add explanations and give an indication of what limitations and assumptions apply. – Brian61354270 Jul 01 '20 at 01:18