在Python中通过参数扩展类

时间:2022-10-03 21:22:37

I want to expand the class Foo by the class Bar, the issue that I have is that I can't expand it in the usual way (class Foo(Bar)) because the class Bar is somewhat dynamically generated.

我想通过类Bar扩展类Foo,我遇到的问题是我无法以通常的方式扩展它(类Foo(Bar)),因为类Bar在某种程度上是动态生成的。

I made this small example to illustrate my desired outcome:

我做了一个小例子来说明我想要的结果:

class Bar:
    def super_cool_function():
        print("Cool")

class Foo:
    def __init__(self, another_class):
        # I want to extend Foo by another_class

# Desired result
foobar = Foo(Bar)
foobar.super_cool_function()

Again this is not what I'm looking for:

这不是我想要的:

class Foo(Bar):
    pass

foobar = Foo()
foobar.super_cool_function()

2 个解决方案

#1


21  

"The class Bar is somewhat dynamically generated" That's fine... as long as it follows the blueprint (of a class that should be extended by Foo), you can leverage python closures here. Dynamically create a new class by creating it inside, and returning it from a function.

“类Bar有点动态生成”这很好......只要它遵循蓝图(应该由Foo扩展的类),你可以在这里利用python闭包。通过在内部创建并从函数返回它来动态创建新类。

def get_class(superclass):
    class Foo(superclass):
        def __init__(self, ...):
           ...

    return Foo

DynamicFoo = get_class(Bar)
myobj = DynamicFoo()

This is a common pattern you'll see in python - leveraging closures to dynamically create callbacks and classes.

这是你在python中看到的常见模式 - 利用闭包来动态创建回调和类。


The answer above assumes that Bar is correctly defined, when it in fact is not. The super_cool_function is missing a self parameter. Instance methods are always called with the first parameter (the instance itself) automatically being passed in as the first attribute.

上面的答案假设Bar是正确定义的,而实际上并非如此。 super_cool_function缺少self参数。始终使用第一个参数(实例本身)作为第一个属性传入实例方法。

So, the correct definition for Bar would be:

因此,Bar的正确定义是:

class Bar:
   def super_cool_function(self):
       print("Cool")

Now, defining get_class with the simplest definition of the inner class Foo:

现在,使用内部类Foo的最简单定义来定义get_class:

def get_class(superclass):
    class Foo(superclass):
        pass

    return Foo

DynamicFoo = get_class(Bar)
myobj = DynamicFoo()
myobj.super_cool_function()
# Cool

#2


4  

Your desired use is a little strange:

你想要的用途有点奇怪:

foobar = Foo(Bar)

You're constructing a Foo instance by handing it the Bar class object, and expecting to get back something that acts like a Bar instance. Normally, a proxy class is designed to take an object to proxy to, or look on up somewhere, not just construct one with no arguments.

您正在构建一个Foo实例,方法是将它交给Bar类对象,并期望得到一些像Bar实例那样的东西。通常,代理类被设计为将对象代理到某个地方,或者在某个地方查找,而不仅仅是构造一个没有参数的对象。

But, other than that oddness, which just means an __init__ method that constructs the object, this is just a bog-standard proxy class. So:

但是,除了奇怪之外,这只是构造对象的__init__方法,这只是一个沼泽标准的代理类。所以:

class Foo:
    def __init__(self, cls):
        self._inst = cls()
    def __getattr__(self, name):
        return getattr(self._inst, name)
    def __setattr__(self, name, value):
        if name in {'_inst'}:
            super().__setattr__(name, value)
        else:
            setattr(self._inst, name, value)
    def __delattr__(self, name):
        delattr(self._inst, name)

Of course you still won't be able to call that super_cool_function on a foobar any more than you could on a Bar instance, because it's defined as a method and doesn't have a self parameter. But you'll get the same error from the Foo instance that you would have gotten from a Bar instance:

当然,你仍然无法在一个foobar上调用super_cool_function比在Bar实例上更多,因为它被定义为一个方法,并且没有自我参数。但是你会从你从Bar实例得到的Foo实例中得到同样的错误:

>>> foobar.super_cool_function
<bound method Bar.super_cool_function of <__main__.Bar object at 0x129f95080>>
>>> foobar.super_cool_function()
TypeError: super_cool_function() takes 0 positional arguments but 1 was 

#1


21  

"The class Bar is somewhat dynamically generated" That's fine... as long as it follows the blueprint (of a class that should be extended by Foo), you can leverage python closures here. Dynamically create a new class by creating it inside, and returning it from a function.

“类Bar有点动态生成”这很好......只要它遵循蓝图(应该由Foo扩展的类),你可以在这里利用python闭包。通过在内部创建并从函数返回它来动态创建新类。

def get_class(superclass):
    class Foo(superclass):
        def __init__(self, ...):
           ...

    return Foo

DynamicFoo = get_class(Bar)
myobj = DynamicFoo()

This is a common pattern you'll see in python - leveraging closures to dynamically create callbacks and classes.

这是你在python中看到的常见模式 - 利用闭包来动态创建回调和类。


The answer above assumes that Bar is correctly defined, when it in fact is not. The super_cool_function is missing a self parameter. Instance methods are always called with the first parameter (the instance itself) automatically being passed in as the first attribute.

上面的答案假设Bar是正确定义的,而实际上并非如此。 super_cool_function缺少self参数。始终使用第一个参数(实例本身)作为第一个属性传入实例方法。

So, the correct definition for Bar would be:

因此,Bar的正确定义是:

class Bar:
   def super_cool_function(self):
       print("Cool")

Now, defining get_class with the simplest definition of the inner class Foo:

现在,使用内部类Foo的最简单定义来定义get_class:

def get_class(superclass):
    class Foo(superclass):
        pass

    return Foo

DynamicFoo = get_class(Bar)
myobj = DynamicFoo()
myobj.super_cool_function()
# Cool

#2


4  

Your desired use is a little strange:

你想要的用途有点奇怪:

foobar = Foo(Bar)

You're constructing a Foo instance by handing it the Bar class object, and expecting to get back something that acts like a Bar instance. Normally, a proxy class is designed to take an object to proxy to, or look on up somewhere, not just construct one with no arguments.

您正在构建一个Foo实例,方法是将它交给Bar类对象,并期望得到一些像Bar实例那样的东西。通常,代理类被设计为将对象代理到某个地方,或者在某个地方查找,而不仅仅是构造一个没有参数的对象。

But, other than that oddness, which just means an __init__ method that constructs the object, this is just a bog-standard proxy class. So:

但是,除了奇怪之外,这只是构造对象的__init__方法,这只是一个沼泽标准的代理类。所以:

class Foo:
    def __init__(self, cls):
        self._inst = cls()
    def __getattr__(self, name):
        return getattr(self._inst, name)
    def __setattr__(self, name, value):
        if name in {'_inst'}:
            super().__setattr__(name, value)
        else:
            setattr(self._inst, name, value)
    def __delattr__(self, name):
        delattr(self._inst, name)

Of course you still won't be able to call that super_cool_function on a foobar any more than you could on a Bar instance, because it's defined as a method and doesn't have a self parameter. But you'll get the same error from the Foo instance that you would have gotten from a Bar instance:

当然,你仍然无法在一个foobar上调用super_cool_function比在Bar实例上更多,因为它被定义为一个方法,并且没有自我参数。但是你会从你从Bar实例得到的Foo实例中得到同样的错误:

>>> foobar.super_cool_function
<bound method Bar.super_cool_function of <__main__.Bar object at 0x129f95080>>
>>> foobar.super_cool_function()
TypeError: super_cool_function() takes 0 positional arguments but 1 was