Scoped metaclasses;或者在B类的__init__期间更改类A的类变量,包含从A继承的嵌套类?

时间:2018-05-18 20:25:18

标签: python metaprogramming metaclass

请考虑以下代码:

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. 我希望尽可能忠实于当前的结构。

2 个答案:

答案 0 :(得分:1)

要解决此问题,只需在self.attr = self.attr __init__函数中添加第A行。由于您不想更改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)

答案 1 :(得分:1)

你疯狂地过度复杂了:

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'