【问题标题】:Python subclass that takes superclass as argument on instantiation?将超类作为实例化参数的 Python 子类?
【发布时间】:2019-09-28 07:16:52
【问题描述】:

我正在尝试在 Python 中创建一个具有以下行为的包装类:

  • 它应该将一个现有类作为参数,它应该从该类继承所有方法和属性
  • 包装类方法应该能够使用 Python super() 访问超类的方法(作为参数传递的方法)

由于我的第二个要求,我认为解决方案 here 是不够的(无论如何,我在深度复制我试图继承的超类的一些方法时遇到了单独的问题)。

我试过了,但它不正确......

class A:
    def shout(self):
        print("I AM A!")

class B:
    def shout(self):
        print("My name is B!")

class wrapper:
    def __init__(self, super_class):
        ## Some inheritance thing here ##
        # I initially tried this but no success...
        super(super_class).__init__() # or similar?

    def shout(self):
        print('This is a wrapper')
        super().shout()

这是我需要的行为...

my_wrapper = wrapper(A)
my_wrapper.shout()
# Expected output: 
# > This is a wrapper
# > I AM A

my_wrapper = wrapper(B)
my_wrapper.shout()
# Expected output: 
# > This is a wrapper
# > My name is B!

继承是否是正确的方法,如果是,我是否朝着正确的方向嗅探?任何帮助表示赞赏,谢谢:)

编辑上下文:

我打算构建多个包装器,以便我的所有 ML 模型都具有相同的 API。通常,来自相同包(例如 sklearn)的模型具有相同的 API,并且应该能够被相同的包装器包装。为此,我希望修改/添加这些模型中现有方法的功能,同时保持相同的方法名称。

【问题讨论】:

  • 包装器必须是一个类吗?似乎只拥有一个创建您想要的类的函数更容易。
  • 你想用这个解决什么问题?有不同的技术上有效的方法来获得类似的结果,但哪种方法是正确的取决于具体的用例......
  • 我打算构建多个包装器,以便我的所有 ML 模型都具有相同的 API。通常,来自相同包(例如 sklearn)的模型具有相同的 API,并且应该能够被相同的包装器包装。为此,我希望修改/添加这些模型中现有方法的功能,同时保持相同的方法名称。
  • 调用super 为另一个对象意味着你依赖它作为一个子类。这会破坏封装,因为您依赖于对象的实现。在您的代码中,既没有超类也没有子类。 TLDR:像这样使用super 绝不是您想要做的。
  • “它应该将一个现有类作为参数,它应该从该类继承所有方法和属性” 继承是​​类之间的关系。您的设置需要一个 instance 从类“继承”。你可能想看看 mixins、proxy 和 wrappers。

标签: python-3.x inheritance


【解决方案1】:

如果包装器必须是一个类,那么组合解决方案将更适合这里。

请记住,我将 shout 方法转换为 staticmethod,因为在您的示例中,您将类传递给 wrapper.shout,而不是实例。

class A:
    @staticmethod
    def shout():
        print("I AM A!")


class B:
    @staticmethod
    def shout():
        print("My name is B!")


class wrapper:
    def __init__(self, super_class):
        self._super_class = super_class

    def __getattr__(self, item):
        try:
            return self.__dict__[item].__func__
        except KeyError:
            return self._super_class.__dict__[item].__func__

    def a_wrapper_method(self):
        print('a wrapper attribute can still be used')


my_wrapper = wrapper(A)
my_wrapper.shout()

my_wrapper = wrapper(B)
my_wrapper.shout()

my_wrapper.a_wrapper_method()

输出

This is a wrapper
I AM A!
This is a wrapper
My name is B!
a wrapper attribute can still be used

【讨论】:

    【解决方案2】:

    所以我最后去了一个函数。我的最终解决方案:

    class A:
        def shout(self):
            print("I AM A!")
    
    class B:
        def shout(self):
            print("My name is B!")
    
    def wrap_letter_class(to_wrap):
    
        global letterWrapper
    
        class letterWrapper(to_wrap):
            def __init__(self):
                super().__init__()
    
            def shout(self):
                print('This is a wrapper')
                super().shout()
    
            def __getstate__(self):
                # Add the wrapper to global scope before pickling
                global letterWrapper
                letterWrapper = self.__class__
                return self.__dict__
    
        return letterWrapper()
    

    这会产生所需的行为...

    In [2]: wrapped = wrap_letter_class(A)
    
    In [3]: wrapped.shout()
    This is a wrapper
    I AM A!
    
    In [4]: wrapped = wrap_letter_class(B)
    
    In [5]: wrapped.shout()
    This is a wrapper
    My name is B!
    

    我最初的问题中没有提到的事情是我打算腌制我的自定义类,如果该类未在全局范围内定义,则这是不可能的,因此 __getstate__ 和全局添加。

    谢谢!

    【讨论】:

      猜你喜欢
      • 2023-04-08
      • 1970-01-01
      • 2013-07-12
      • 2015-05-29
      • 2019-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-17
      相关资源
      最近更新 更多