简体   繁体   中英

How do I define python methods for generated classes dynamically?

I am trying to implement a way to generate many classes with different methods that depend on a parameter. What I currently have is:

class Base:
    def __init__(self, y):
        self.y = y

    def method(self, x):
        raise NotImplementedError()


class KlassMethod:
    def __init__(self, a):
        self.a = a

    def __call__(self, base, x):
        return x * self.a + base.y


klass = type("Klass5", (Base,), {'method': KlassMethod(5)})
obj = klass(8)
obj.method(5)

In short, I need the class KlassMethod for the method because I need to encapsulate the parameter a inside, because it can be different for different klass . On the other hand, I need to have the access to the object's attribute y . When I run the code above I get the error:

TypeError: __call__() missing 1 required positional argument: 'x'

If I didn't have the parameter a , I would implement the method as a function like this, that works fine:

def method(base, x):
    return x + base.y

As the solution, I also tried to use functools.partial like this:

from functools import partial

...

def klass_method(self, x, a):
    return x * a + self.y

klass = type("Klass", (Base,), {'method': partial(klass_method, a=5)})
...

The error is the same.

It looks like __call__ does not work as expected if we call it with the instance. How do I solve the issue?

I think this does what you want:

def make_class(a):
    class Klass(Base):
        def method(self, x):
            return x * a + self.y
    return Klass

klass = make_class(5)

Here the a is closed over, but you could also make it work as a class attribute:

def make_class2(a):
    class Klass(Base):
        def method(self, x):
            return x * self.a + self.y
    Klass.a = a
    return Klass

I think it is generally simpler to write a local class declaration than to dynamically call the type constructor, but if you want to do it that way (eg because each dynamic class having different class names is important to you) then that's still possible:

def make_class(a):
    def method(self, x):
        return x * a + self.y
    return type('Klass5', (Base,), {'method': method})

def make_class2(a):
    def method(self, x):
        return x * self.a + self.y
    return type('Klass5', (Base,), {'method': method, 'a': a})

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