覆盖父名称空间变量

时间:2020-03-23 12:52:34

标签: python namespaces metaclass

在尝试研究元类之前,我试图了解Python类。我遇到了一些我不知道的代码。在这种情况下,类不是使用 self ,而是使用类名称空间(没有选择使用self的选项,因此这里存在问题)。如何在一个子类中定义一些名称空间变量,然后在子类中重写它们都依赖的值?

首例

class B():
    potato = "hey"
    test = potato
    #here would go a lot of more code that just depends on that potato value

class c(B):
    B.potato = "not hey"

c_potato = c().test
print(c_potato)

它显示。据我了解,因为 test 指向的字符串“ hey”,这是不可变的。更改B.potato = "not hey"仅将类名称空间 potato 更改为新字符串,但不会更改 test 指向的内容。所以我想,嘿,如果我用列表来做,那是引用的权利?

class B():
    potato = ["hey"]
    test = potato[0]

class c(B):
    B.potato[0] = "not hey"

c_potato = c().test
print(c_potato)

在我看来,这应该有效。我没有更改马铃薯指向的内容,而是更改了值。没有?但我知道它不会实际上是有效的,因为 test 指向的是土豆[0] ,而不仅仅是土豆 。是的,我知道为什么它还会打印

然后我意识到,如果 test 需要指向不可更改的结果,那么我试图使用名称空间是不可能的。

class B():

    @staticmethod
    def get_potato():
      return "hey"

    potato = get_potato.__func__
    test = potato()

class c(B):

    @staticmethod
    def get_potato():
      return "not hey"

    B.potato = "6"

c_potato = c().test
print(c_potato)

我在这里更改了 B.potato entire 值,但是现在 test 已经指向了父级的结果potato(),所以没关系,仍然会打印出“嘿”

那么我想,元类可以解决这个问题吗?显然可以。

class Meta(type):
    def __new__(cls, name, bases, attrs):
        x = super().__new__(cls, name, bases, attrs)
        x.potato = "basic hey"
        if 'meta_args' in attrs:
            x.potato = attrs['meta_args'][0]
            del attrs['meta_args'] # clean up
        x.test = x.potato    
        return x

class A():
  pass

class B(A, metaclass=Meta):
  meta_args = ["super hey"]
  pass

class C(B):
    meta_args = ["not hey"]

b = B().test
c = C().test
print(b)
print(c)

这将正确打印 b 超级嘿 c 不嘿。问题是,可以在没有元类的情况下完成此操作吗?这时我的脑痛。

2 个答案:

答案 0 :(得分:1)

您可能想要一个@property

class B:
    potato = 'hey'

    @property
    def test(self):
        return self.potato


class C(B):
    potato = 'not hey'

您所做的是以一种或另一种方式将"hey"分配给test。除非您为test实际分配了其他内容,否则它的值不会改变。通过将其设为@property,每次访问def test时都会调用函数.test,因此可以动态计算其值;在这种情况下,基于potato的值。子类声明了自己的potato,它遮盖了父级的potato属性。

答案 1 :(得分:0)

如您所见,您对“令人惊讶的”结果做了很多实验,并做了一些简单的事情,例如将列表的第一个元素分配给变量,以释放您对该问题的错误观念。

为什么您认为自己“钉住”它并在简单操作时正确使用它,而“查找”您可以使用元类“更改值”呢?

否-继续沿您以前的趋势发展,您仍然有错误的假设。

元类__new__方法中的代码,就像类 body 中的代码在创建每个类时运行一次。在此之后,将创建,分配和更改变量。

您的“元类实验”为potatotest属性创建一个新值,方法是在新类(C)为创建。

没有什么“元”与这样做有所不同:

class B:
    potato = 0

class C(B):
   pass

C.potato = 1

print(C.potato)   # Hey it works!

如果您想“神奇地改变值”,就像电子表格程序上的计算值一样,Python提供了一种机制来做到这一点,并且它与元类无关:它们是“描述符”。其中最知名的是使用内置的property创建的。

因此,描述符可以做您尝试探索时所做的事情:它们看起来像是在阅读或书写时的普通属性,但是可以触发“幕后”的任何代码并由它们来计算。它们确实是一种可用于实现“反应式编程”的多个方面的机制。

但是,由于property是最流行的形式,不应该被使用,而是被设计为可用于类实例,而不是愚蠢地将类用作命名空间。

因此,我建议您切换到该位置,您将有更多的探索要做:

class B:
    def __init__(self):
        self.potato = Hey

    @property
    def test(self):
        return self.potato

c = B()
print(c.test)
c.potato = "not hey"
print(c.test)