user1477337
user1477337

Reputation: 395

How to make an array of functions callable in python

I'm trying to create a class A which is basically a list of objects B. I would like to be able to call a method in A which automatically returns a list of the corresponding method in B:

A.method(x) = [B.method(x) for B in A]

The issue is that I need a dynamical behavior, so any method in B is automatically "inherited" by A without having to hard code it.

I have tried using lambda functions and exec("""def ..."""), but nothing seems to work. Here is an attempt:

class A(object):

    def __init__(self,Bs):
        self.listOfBs = Bs[:]

        if self.listOfBs:
            for method_name in dir(self.listOfBs[0]):
                if not callable(getattr(self.listOfBs[0],method_name)):
                    continue

            f = lambda x: [getattr(B,method_name)(x) for B in self.listOfBs]
            setattr(self,method_name,f)

class B(object):
    def __init__(self,name):

        self.name = name

    def getName(self,x):
        return self.name+x

#So if I define:
a = A([B('x'),B('y'),B('z')])

#I would like to have: a.getName('W') = ['xW','yW','zW']
#However I get the error: TypeError: 'str' object is not callable

I think there should be an easy/elegant way of implementing the above behavior in python, but I couldn't find anything that works.

Upvotes: 1

Views: 1369

Answers (2)

user1477337
user1477337

Reputation: 395

Thanks a lot. I had tried getattr before, but was missing some steps. Just for the record, following Glazner's suggestion here is a working solution, which works both with attributes and methods:

class A(object):

    def __init__(self,Bs):
        self.listOfBs = Bs[:]

    def __getattr__(self, attr):

        if not all(hasattr(b,attr) for b in self.listOfBs):
            raise AttributeError("Attribute %s not found." %attr)


        val = getattr(self.listOfBs[0],attr)
        if not callable(val):
            return np.array([getattr(b,attr) for b in self.listOfBs])

        def call(*args, **kw):
            return [getattr(b, attr)(*args, **kw) for b in self.listOfBs]
        return call        

class B(object):
    def __init__(self,name):

        self.name = name

    def getName(self,x):
        return self.name+x


a = A([B('x'),B('y'),B('z')])
a.name #Returns ['x','y','z']
a.getName('W')  #Returns ['xW','yW','zW']

Upvotes: 2

Yoav Glazner
Yoav Glazner

Reputation: 8066

You may use __getattr__ to make method lookup dynamic

class A:
    def __init__(self, bs):
        self.bs = bs

    def __getattr__(self, method_name):
        def call(*args, **kw):
            return [getattr(b, method_name)(*args, **kw) for b in bs]
        return call

Upvotes: 3

Related Questions