質問

I have class:

class A(object):
    def do_computing(self):
        print "do_computing"

Then I have:

new_class = type('B', (object,), {'a': '#A', 'b': '#B'})

What I want to achieve is to make all methods and properties on class A a member of class B. Class A can have from 0 to N such elements. I want to make them all a member of class B.

So far I get to:

methods = {}

for el in dir(A):
    if el.startswith('_'):
        continue

    tmp = getattr(A, el)

    if isinstance(tmp, property):
        methods[el] = tmp

    if isinstance(tmp, types.MethodType):
        methods[el] = tmp

instance_class = type('B', (object,), {'a': '#A', 'b': '#B'})

for name, func in methods.items():
    new_method = types.MethodType(func, None, instance_class)
    setattr(instance_class, name, new_method)

But then when I run:

instance().do_computing()

I get an error:

TypeError: unbound method do_computing() must be called with A instance as first argument (got B instance instead)

Why I had to do that? We have a lot of legacy code and I need fancy objects that will pretend they are old objects but really.

One more important thing. I cannot use inheritance, to much magic happens in the background.

役に立ちましたか?

解決

If you do it like this, it will work:

import types

class A(object):
    def do_computing(self):
        print "do_computing"

methods = {name:value for name, value in A.__dict__.iteritems()
                        if not name.startswith('_')}

instance_class = type('B', (object,), {'a': '#A', 'b': '#B'})

for name, func in methods.iteritems():
    new_method = types.MethodType(func, None, instance_class)
    setattr(instance_class, name, new_method)

instance_class().do_computing()

他のヒント

Unless I'm missing something, you can do this with inheritance:

class B(A):
    def __init__(self):
        super(B, self).__init__()

Then:

>>> b = B()
>>> b.do_computing()

do_computing

Edit: cms_mgr said the same in the comments, also fixed indentation

are you creating a facade? maybe you want something like this:

Making a facade in Python 2.5

http://en.wikipedia.org/wiki/Facade_pattern

you could also use delegators. here's an example from the wxpython AGW:

_methods = ["GetIndent", "SetIndent", "GetSpacing", "SetSpacing", "GetImageList", "GetStateImageList",
        "GetButtonsImageList", "AssignImageList", "AssignStateImageList", "AssignButtonsImageList",
        "SetImageList", "SetButtonsImageList", "SetStateImageList", 'other_methods']

def create_delegator_for(method):
"""
Creates a method that forwards calls to `self._main_win` (an instance of :class:`TreeListMainWindow`).

:param `method`: one method inside the :class:`TreeListMainWindow` local scope.
"""

    def delegate(self, *args, **kwargs):
        return getattr(self._main_win, method)(*args, **kwargs)
    return delegate

# Create methods that delegate to self._main_win. This approach allows for
# overriding these methods in possible subclasses of HyperTreeList
for method in _methods:
    setattr(HyperTreeList, method, create_delegator_for(method))    

Note that these wrap class methods... i.e both functions take a signature like def func(self, some, other, args) and are intended to be called like self.func(some, args). If you want to delegate a class function to a non-class function, you'll need to modify the delegator.

You can inherit from a parent class as such:

class Awesome():

    def method_a():
        return "blee" 


class Beauty(Awesome):

    def __init__(self):
        self.x = self.method_a()

b = Beauty()
print(b.x)
>>> "blee"

This was freely typed, but the logic is the same none the less and should work.

You can also do fun things with setattr like so:

#as you can see this class is worthless and is nothing 
class blee():
    pass 

b = blee()
setattr(b, "variable_1", "123456")
print(b.variable_1)
>>> 123456

essentially you can assign any object, method to a class instance with setattr.

EDIT: Just realized that you did use setattr, woops ;)

Hope this helps!

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top