开发者

python, wrapping class returning the average of the wrapped members

开发者 https://www.devze.com 2022-12-24 10:03 出处:网络
The title isn\'t very clear but I\'ll try to explain. Having this class: class Wrapped(object): def method_a(self):

The title isn't very clear but I'll try to explain.

Having this class:

class Wrapped(object):
    def method_a(self):
        # do some operations
        return n

    def method_b(self):
        # also do some operations
        return n

I want to have a class that performs the same way as this one:

class Wrapper(object):
    def __init__(self):
        self.ws = [Wrapped(1),Wrapped(2),Wrapped(3)]

    def method_a(self):
        results=[Wrapped.method_a(w) for w in self.ws]
        sum_ = sum(results,0.0)
        average = sum_/len(self.ws)
        return average

    def method_b(self):
        results=[Wrapped.method_b(w) for w in self.ws]
        sum_ = sum(results,0.0)
        average = sum_/len(self.ws)
        return average

Obviously this is not the actual problem at hand (it is not only two methods), and this code is also incomplete (only included the m开发者_高级运维inimum to explain the problem).

So, what i am looking for is a way to obtain this behavior. Meaning, whichever method is called in the wrapper class, call that method for all the Wrapped class objects and return the average of their results.

Can it be done? How?

Thanks in advance.

Edit

Thanks for the answers... after seeing them the solution it all seems obvious :) I have chosen Alex Martelli's answer because it explained the solution well. The other answers where also useful, that's why I also voted them up.


While quite doable, it's just a little bit tricky because the getting of a method (or other attribute) and the calling thereof are separate operations. Here's a solution:

class Wrapper(object):
    def __init__(self):
        self.ws = [Wrapped(1),Wrapped(2),Wrapped(3)]

    def __getattr__(self, n):
        meth = getattr(Wrapped, n)
        def caller():
            results = [meth(w) for w in self.ws]
            sum_ = sum(results,0.0)
            average = sum_/len(self.ws)
            return average
        return caller

It's a bit simplistic (assumes no change in self.ws between the getting and the calling -- one could of course "snapshot" self.ws at the moment of the getting, if that's the desired semantics; doesn't use @functools.wraps so doesn't preserve the docstring &c if those need preserving), but should be mostly workable for your purposes.


You can to this (call an 'arbitary' method on an object) with getattr:

Consider this as example:

# this fetches you references to the 'method_a' methods for objects in self.ws
funcs = [getattr(wrapped_obj, "method_a") for wrapped_obj in self.ws]
# now execute them to get the results
results = [func() for func in funcs]

The rest of your code would stay the same.


You could do this by implementing a __getattr__ method in your Wrapper which returns a function which forwards the call to all wrapped objects. Here's an example of a basic implementation simply returning the results:

import functools

class Wrapper(object):
    def __init__(self, *args):
        self._targets = args

    def _broadcast(self, name, *args, **kwargs):
        return [getattr(t, name)(*args, **kwargs) for t in self._targets]

    def __getattr__(self, name):
        return functools.partial(self._broadcast, name)

Note that __getattr__ will only be called when an attribute is not found, so all method names that you define in Wrapper won't be forwarded (such as _broadcast in above example).


You could do this using the special getattr method.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号