【问题标题】:descriptor '__dict__' for objects doesn't apply to object using type()对象的描述符“__dict__”不适用于使用 type() 的对象
【发布时间】:2020-07-26 14:25:35
【问题描述】:

在 Python 3.6+ 中使用 type() 时出现奇怪的行为。我正在使用它来动态包装来自外部库的所有类,并且在调用self.__dict__ 时出现错误。考虑以下示例:

class A(object):
    def __init__(self):
        print(self.__dict__)

class B(A):
    def __init__(self):
        super(self.__class__, self).__init__()

B() # this works

但是当我使用type 做同样的事情时,我得到了这个奇怪的错误:

class A(object):
    def __init__(self):
        print(self.__dict__)

class B(object):
    def __init__(self):
        super(self.__class__, self).__init__()

# C is a class which is B that inherits from A
C = type('C', (A,), B.__dict__.copy())
C()
# TypeError: descriptor '__dict__' for 'B' objects doesn't apply to 'C' object

如何使用type() 解决此问题?

[TLDR:@fritzo 的回答修复了错误,但这种设计引入了其他继承问题。 @juanpa.arrivillaga 接受的答案提供了更好的解决方法]

【问题讨论】:

  • 不同的问题,但不要将self.__class__type(self) 传递给super - it breaks as soon as you create a grandchild class
  • 我在非 __init__ 方法中需要它,否则我会得到 TypeError: super(type, obj): obj must be an instance or subtype of type
  • 确实发生了该错误。不过,孙辈是为工作而设计的,而您所做的却不是。你所做的与super 不兼容,并且在很多其他情况下都会中断,而不仅仅是super__dict__。你真的应该重新设计你的包装器;如果你不这样做,问题就会不断堆积,尤其是在外部库发生变化时。
  • 根据程序其余部分的上下文,正确的解决方案可能是给您的C 一个B 实例作为属性,或者使A 混合从AB 多重继承。
  • 所以我尝试使用 mixins,但无法获得我想要的功能。假设我有一个来自类 C(B, A) 的方法foo(),其中 B 是我的 mixin,A 是外部类。在foo() 中,我想检查一个属性是否存在并调用B.foo 否则调用A.foo。但是在 B 中,我无法访问 A 的类(和方法)。这对于一个类来说很简单,只需在没有 type 的情况下将其写出来,但我想以编程方式为所有类执行此操作。这个怎么写?

标签: python python-3.x oop inheritance


【解决方案1】:

根据您希望动态执行此操作的 cmets,您可以使用 super,但您必须了解它是如何工作的,super 为您提供方法解析顺序中的下一个方法,所以你确实可以从你的mixin访问A中的方法,但你必须正确使用super:所以像这可以工作:

In [1]: class Mixin:
   ...:     def foo(self):
   ...:         if hasattr(self, 'whatever'):
   ...:             print("doing Mixin.foo stuff")
   ...:         else:
   ...:             return super().foo()
   ...:

In [2]: class A:
   ...:     def foo(self):
   ...:         print("doing A.foo stuff")
   ...:

In [3]: class C(Mixin, A):
   ...:     pass
   ...:

In [4]: c = C()

In [5]: c.foo()
doing A.foo stuff

In [6]: c.whatever = 42

In [7]: c.foo()
doing Mixin.foo stuff

但是,我不确定这是否值得推荐。但这是可能的。

注意,我所说的动态是指这样的工作:

In [9]: class Mixin:
   ...:     def foo(self):
   ...:         if hasattr(self, 'whatever'):
   ...:             print("doing Mixin.foo stuff")
   ...:         else:
   ...:             return super().foo()
   ...: class A:
   ...:     pass
   ...:
   ...: class B:
   ...:     def foo(self):
   ...:         print("doing B.foo stuff")
   ...:
   ...: class C(Mixin, A, B):
   ...:     pass
   ...:

In [10]: c = C()

In [11]: c.foo()
doing B.foo stuff

In [12]: c.whatever = 42

In [13]: c.foo()
doing Mixin.foo stuff

【讨论】:

  • 感谢您的回复!我有一堆不同的As 我正在加载,所以以编程方式(AFAIK)执行此操作的唯一方法是使用C = type('C', (Mixin, A), Mixin.__dict__.copy()),这与我在 OP 中的内容有关。但是,我在super().foo() 中遇到类型错误,上面的评论中有 TypeError。我可以用super(self.__class__, self) 解决它,但是根据我们上面的对话,这是不可取的。
  • @luxon 如果您对您所面临的实际问题进行完整完整的描述,将会有所帮助。这是X-Y problem 的类示例
  • 啊,是的,我的意思是C = type('C', (Mixin, A), {})。我之前在调用super() 时遇到了问题,但我相信这个解决方案有效。谢谢!我将编辑并接受您的回复;我尽量避免编辑 OP,因为其他人可能只想调试具有不同问题的错误消息。
  • 让我重申我的高级问题。有一个外部库,其中包含许多共享一个共同父级的类。我想重写 every 类中的一些方法,以便在满足条件时执行自定义逻辑,否则使用原始逻辑。在这个上游库中可以添加新的类,所以我想动态地这样做(所以在你的例子中想象有 {A_1, A_2, ... A_n} 其中 n 是类的数量)
  • @luxon 那么您可以只使用类定义语句。 for cls in list_of_classes: class X(Mixin, cls): pass;
猜你喜欢
  • 2021-09-14
  • 2020-08-12
  • 2020-03-18
  • 2022-01-21
  • 2020-11-01
  • 2021-10-26
  • 1970-01-01
  • 2022-11-26
  • 2020-06-30
相关资源
最近更新 更多