【问题标题】:Scoped metaclasses; or changing class variable of class A during the __init__ of class B containing nested classes inheriting from A?范围元类;或在包含从 A 继承的嵌套类的 B 类的 __init__ 期间更改 A 类的类变量?
【发布时间】:2018-10-29 08:03:31
【问题描述】:

考虑下面的代码:

class A(object):
    attr = None

    def __init__(self):
        assert A.attr is not None


class B(object):
    def __init__(self, attr):
        A.attr = attr

    class C(A):
        def __init__(self):
            super().__init__()

    class D(A):
        def __init__(self):
            super().__init__()

    nested_classes = {cls.__name__: cls for cls in {C, D}}

上面似乎没有按我的预期工作,因为:

>>> first_class = B("first")

>>> first_sub_class = first_class.C()

>>> first_sub_class.attr
'first'

>>> second_class = B("second")

>>> second_sub_class = second_class.C()

>>> second_sub_class.attr
'second'

>>> first_sub_class.attr
'second'

有没有办法让first_sub_class.attr 成为first 而让second_sub_class.attr 成为second?可能是因为有一个范围在 B 内的元类?

几点:

  1. 我不想传递 attr,我想在 B 初始化时设置它。
  2. 我不想通过使用partial 来绕过上面的点,因为它会破坏依赖__name____qualname__ 或类似的代码的其余部分。
  3. 我想尽可能忠实于当前的结构。

【问题讨论】:

  • 这些类都不是元类。
  • 没有pythonic方式。只是传递它。
  • @Aran-Fey 从某种意义上说,它们都不是元类,它们不使用__metaclass__。很明显C(A)如果没有设置attr就不能创建。无论您是否使用__metaclass__ 编写它都不会对我的目的产生影响,如果它让您满意,我可以重写示例。我想看看我是否可以让 A 没有像现在一样被动态限定范围!
  • @MegaIng 不在乎什么是 Pythonic。只要我不必进行字符串插值和exec,我就可以有一个解决方案。
  • 我不知道您打算将此代码用于什么目的,但我确实知道不应该这样做。从在另一个类的构造函数中设置类属性到通过实例访问该类属性,这段代码的一切都有味道。我建议您退后一步,询问您的实际 问题,而不是询问如何修复此代码。不会有任何好处。

标签: python metaprogramming metaclass


【解决方案1】:

要解决这个问题,只需在A__init__ 函数内添加行self.attr = self.attr。由于您不想更改A 的属性,因此您必须进行以下更改:

class A(object):
    attr = None

    def __init__(self):
        assert self.attr is not None # Don't refer to A, but to self to get the correct value
        self.attr = self.attr


class B(object):
    def __init__(self, attr):
        self.attr = attr # Don't edit A, just save the value in own instance

    def __getattribute__(self, item): # completely added, does some magic to ensure class.attr is set correctly 
        if item in B.nested_classes:
            c = B.nested_classes[item]
            c.attr = self.attr
            return c
        return super().__getattribute__(item)

    class C(A):
        def __init__(self):
            super().__init__()

    class D(A):
        def __init__(self):
            super().__init__()

    nested_classes = {cls.__name__: cls for cls in {C, D}}


first_class = B("first")
first_sub_class = first_class.C()
print(first_sub_class.attr)
second_class = B("second")
second_sub_class = second_class.C()
print(second_sub_class.attr)
print(first_sub_class.attr)

【讨论】:

  • 那行不通。我根本不希望 A 被实例化。但我想看看是否可以在不传递参数的情况下实例化 B.CB.D。如果我可以定义 CD 以使它们继承自已修改但位于 B.__init__ 的 A,那么您的解决方案将会奏效,但这是不可能的。
  • 对于这个例子它可以工作,只添加了这一行。至少尝试一下或举一个不起作用的例子。 A is 也被实例化了。你认为super().__init__() 会做什么?
  • @Megalng 这个例子是我已经拥有的代码,它的行为不像我想要的那样!如果有人这样做A(),那么就会得到一个断言错误。如果一个是B(),然后是B.C(),然后是A(),那么一个不会......这不是我想要的! B 有没有办法控制 C 和 D 的创建方式?
  • 是的,但这会很复杂,不应该这样做。 你的下属问题是什么? 这个问题脱离上下文没有意义。在您提供的上下文中,此解决方案有效。
  • @Megalng 没有潜在问题。我试图看看我可以将它弯曲多远,而无需求助于 C 或 D 中的帧跳转或重新使用字符串插值和执行程序。如果我想要的是可能的,那我该怎么做呢?
【解决方案2】:

你太复杂了:

class A:
    def __init__(self, attr):
        self.attr = attr

class C(A):
    pass

class D(A):
    pass

class B:
    def __init__(self, attr):
        self.attr = attr

    def C(self):
        return C(self.attr)

    def D(self):
        return D(self.attr)

行为完全符合要求:

>>> first_class = B("first")

>>> first_sub_class = first_class.C()

>>> first_sub_class.attr
'first'

>>> second_class = B("second")

>>> second_sub_class = second_class.C()

>>> second_sub_class.attr
'second'

>>> first_sub_class.attr
'first'

【讨论】:

  • 错了。例如,CD 必须继承自 A 并获取其所有方法。同样,我正在尝试在 B 的范围内更改 CD 的类继承自(不一定是 B,就像它们从您的解决方案中继承一样),仅此而已。
  • @Atonal 他们是如何从B 继承的?它们继承自 AC(A)D(A)
  • def C(self) 等是函数的坏名称
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-06
  • 2022-12-18
  • 1970-01-01
  • 2015-01-01
  • 1970-01-01
相关资源
最近更新 更多