简体   繁体   中英

Create a new class definition with different default arguments, using some other class definition?

Say I have the following class in Python 2.7:

class X():
    def __init__(self, alpha=1):
        self.alpha = alpha
        print self.alpha

    def beta(self, gamma=1):
        self.gamma = gamma
        self.omega = self.alpha + self.gamma
        print self.omega

I want to use the class definition to create another class definition with different default arguments, eg something like:

Y = f(X, alpha=2, gamma=2)

or

Y = f(X, __init__.alpha=2, beta.gamma=2)

which should be equivalent to:

class Y():
    def __init__(self, alpha=2):
        self.alpha = alpha
        print self.alpha

    def beta(self, gamma=2):
        self.gamma = gamma
        self.omega = self.alpha + self.gamma
        print self.omega

Is it possible to do something like this in Python 2.7 (or 3?)?

(I know you can use the functools.partial to do the equivalent for functions; so I was wondering if there was anything similar for classes)

You can write a function that creates classes for you:

def makeclass(alpha, gamma):
    class C():
        def __init__(self, alpha=alpha):
            self.alpha = alpha
            print self.alpha

        def beta(self, gamma=gamma):
            self.gamma = gamma
            self.omega = self.alpha + self.gamma
            print self.omega

    return C
>>> X = makeclass(1, 1)
>>> Y = makeclass(2, 2)
>>> x = X() # X is the class, x is an instance
1
>>> x.beta()
2
>>> y = Y()
2
>>> y.beta()
4

This can be done, but it's a bit messy.

This code:

class Foo(base1, base2, ...):
    bar = something
    baz = something_else
    def qux(self):
        ...

...is equivalent to this code, though I'm not sure this is correct if Foo is a classic class:

Foo = type('Foo', (base1, base2, ...), {'bar': something, 'baz': something_else,
                                        'qux': lambda self: ...})

So we can create classes on the fly, and attach custom method objects to them.

For your specific case, we need to make these calls (assuming you convert X into a new-style class by inheriting from object ). First, we extract methods from X and apply functools.partial :

new_init = functools.partial(X.__init__.im_func, # Just X.__init__ in 3.x
                             alpha=2)
new_beta = functools.partial(X.beta.im_func, # Just X.beta in 3.x
                             gamma=2)

The im_func attribute grabs the underlying function out of the unbound method object. In Python 3, there's no such thing as an unbound method, so we don't need to use this attribute; X.__init__ is just the function itself.

Next, we create the class:

Y = type('Y', (object,), {'__init__': new_init, 'beta': new_beta})

Unfortunately, we don't have any reasonable way of getting a list of the functions we need to redefine here. If we use dir() we'll get a lot of irrelevant special attributes. We could use the class's __dict__ , but that won't include any inherited methods. I think we may need to search X.__mro__ for a fully correct listing of methods:

result = {}
for class_ in X.__mro__:
    for name, value in class_.__dict__.items():
        if name not in result:
            result[name] = value

We also need to figure out which arguments to override in each case; inspect.getargspec() is a good starting point for this, but it will not help much if any of the methods are variadic (ie if they take *args or **kwargs arguments).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM