【问题标题】:Inherit from a class given as an argument从作为参数给出的类继承
【发布时间】:2020-10-05 07:37:42
【问题描述】:

我的设置:我在 windows10 机器上使用 spyder4 编写 python 3.7。

上下文

我正在为一个包编写类。它是我正在导入的另一个包中定义的类的某种处理程序。

这里是情况的简化版本:

# This I cannot modify, as they are in the package
class Letter_Class():
    def __init__(self, name):
        self.name = name

class A(Letter_Class):
    def source(self):
        print("from class A")
    def funcA(self):
        print("from class A")

class B(Letter_Class):
    def source(self):
        print("from class B")
    def funcB(self):
        print("from class B")

class C(Letter_Class):
    def source(self):
        print("from class C")
    def funcC(self):
        print("from class C")

# This I can modify
class Dynamic(Letter_Class):
    def __new__(self, name, this_class): # -------- issue lies here? --------
        obj = this_class(name)
        return obj

    def new_func(self):
        print("function defined in dynamic class")

Letter_Class、A、B、C 类已经由其他人定义(在我正在处理的“其他包”中),我无法更改它们。我想创建一个类,这里​​称为“Dynamic”,它将从作为参数给出的类继承。它必须仍然具有我希望添加的(很多很多)方法,例如new_func()。我无法提前知道使用了哪个Letter_Class child。

类A、B、C既有多态方法又有特定方法,例如source()和funcA()。因此,我不能让Dynamic 从这些类继承。这太容易了。

问题

如果我执行前面和下面的代码:

# user executes in main: 
instance = Dynamic("test name", B)
print(instance.name)
instance.source()
instance.funcB()
instance.new_func()

我明白了:

test name  
from class B  
from class B  

AttributeError: 'B' object has no attribute 'new_func'  

实例创建完成,source() 和 funcB() 执行没有任何问题。 然后,引发属性错误,因为没有真正创建 Device 对象。

谁能向我解释在__new__() 构造函数中放入什么来实际返回Device 类的实例,并从this_class 正确继承?我将不胜感激。
This discusion 似乎解决了这个问题,但在不同的情况下,我无法应用建议的解决方案。

【问题讨论】:

  • " 我想创建一个类,这里​​称为 "Dynamic",它将从作为参数给出的类继承。"这没有多大意义。继承发生在定义类时而不是创建实例时。可能有一些 hacky 方法可以完成此操作,但您几乎肯定不应该这样做。很可能,您只需要某种代理类。
  • 所以,这是一个very similar question,我在其中给出了一个答案,演示了如何定义代理类。
  • @juanpa.arrivillaga 感谢您的链接。我想出了一个像你这样的解决方案,通过将调用重定向到我想要包装的类的私有变量。我缺少 __getattr__() 特殊方法,这完美地解决了问题。代理类正是我正在寻找的,我会看看我是否可以在我的项目中实现它。非常感谢
  • 请注意,这样的代理不适用于 dunder 方法,例如__eq__ 或 __str__。通常这很好...here's 一种解决方法,如果不是,我准备了一些其他答案,如果这对您来说是个问题...

标签: python class oop inheritance arguments


【解决方案1】:

问题是您将instance 设置为等于Letter_Class 的返回值; instance 是一个B 对象,与Dynamic 没有关系。

【讨论】:

  • 是的,这正是我所发生的事情,否则我不知道该怎么办。我没有明确表示,谢谢
【解决方案2】:

您可以使用工厂函数来构建您的类实例,并使用给定的类来继承:

class Letter_Class:
    def __init__(self, name):
        self.name = name

class A(Letter_Class):
    def source(self):
        print("from class A")
    def funcA(self):
        print("from class A")

class B(Letter_Class):
    def source(self):
        print("from class B")
    def funcB(self):
        print("from class B")

class C(Letter_Class):
    def source(self):
        print("from class C")
    def funcC(self):
        print("from class C")


def make_dynamic_letterclass_factory(cls):
    """makes an returns a Letter_Class subclass instance that inherits
    from the class passed as argument
    """
    class Dynamic(cls):
        def __init__(self):
            pass
        def new_func(self):
            print("function defined in dynamic class")

    return Dynamic()


a = make_dynamic_letterclass_factory(A)
a.source(), a.funcA()

b = make_dynamic_letterclass_factory(B)
b.source(), b.funcB()

c = make_dynamic_letterclass_factory(C)
c.source(), c.funcC()

输出:

from class A
from class A
from class B
from class B
from class C
from class C

【讨论】:

  • 这似乎可以解决问题!但是,按照我设计这个包的方式,我宁愿使用@juanpa.arrivillaga 在更高评论中提出的代理类。我很快就会更新我的最终解决方案
【解决方案3】:

解决方案

我找到了解决我的问题的方法(感谢 SO 社区)。对于所有寻找THEIR问题解决方案的编码人员,该用例可能会让您想选择以下两种解决方案之一:

工厂函数

使用构建类的工厂函数 - 请参阅 Reblochon Masque 对此的回答。

使用代理类

这是 juanpa.arrivillaga 在this question 中提出的建议。

这是我相信我将从现在开始使用的解决方案:

# classes Letter_Class, A, B, and C are the same as in the "Context" section (above)  
class Proxy():
    """ attempt to make a transparent proxy
    """
    def __init__(self, name, letterclass, *args, **kwargs):
        self.letter = letterclass(name, *args, **kwargs)

    def __getattr__(self, attribute):
        return getattr(self.letter, attribute)


class Dynamic(Proxy):
    """ My actual class and its methods
    """
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # anything else to initiate the instance

    def new_func(self):
        print("calling a method defined in the Dynamic class")


我现在可以执行了:

instance = Dynamic("foo bar", B)
print(f"instance is called: '{instance.name}'")
instance.source()
instance.funcB()
instance.new_func()

我会得到:

instance is called: 'foo bar'
from class B
from class B
calling a method defined in the Dynamic class

对Dynamic 类中定义的NOT 属性的任何调用都将重定向到self.letter,使用神奇的方法:__getattr__()。改用__getattribute__() 会将ALL 调用重定向到self.letter。

我之所以选择这种方法,是因为我希望我的用户能够构建自己的 Dynamic 类;我发现制作一个中间代理类更简单。

此资源也很有帮助,并且提供了制作更透明代理类的线索:Object proxying。不过我并没有尝试实现这一点。

所以,谢谢大家的帮助,保重。
XC

【讨论】:

    猜你喜欢
    • 2020-03-05
    • 1970-01-01
    • 1970-01-01
    • 2014-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-05
    • 1970-01-01
    相关资源
    最近更新 更多