覆盖父命名空间变量
Overriding parents namespace variable
在深入研究元classes 之前,我试图理解Python classes。我遇到了一些我无法理解的代码。在这种情况下,classes 没有使用 self 而是 class 命名空间(没有使用 self 的选项,因此这里有问题)。我如何在一个 class 中定义一些命名空间变量,然后在子 class 中覆盖它们都依赖的值?
第一个案例
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)
它打印 hey。我理解是因为 test 指向字符串 "hey",它是不可变的。更改 B.potato = "not hey"
只会将 class 命名空间 potato 更改为新字符串,但不会更改 test 指向的内容至。所以我想,嘿,如果我用一个列表来做,那是通过引用吗?
class B():
potato = ["hey"]
test = potato[0]
class c(B):
B.potato[0] = "not hey"
c_potato = c().test
print(c_potato)
在我看来,这应该有效。我没有更改 potato 指向的内容,而是更改了值。不?但我知道它 wont' 实际上有效,因为 test 指向 potato[0] 而不是只是 土豆。所以是的,我明白为什么这也会打印 hey.
然后我意识到,如果 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() 的结果,所以没关系,仍然打印出 "hey"。
然后我想,metaclasses 可以解决这个问题吗?显然是的,它可以。
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)
并且正确打印 super hey fo b 和 not hey for c。问题是,这可以在没有 metaclasses 的情况下完成吗?脑袋都疼了
你可能想要 @property
:
class B:
potato = 'hey'
@property
def test(self):
return self.potato
class C(B):
potato = 'not hey'
您所做的是以某种方式将 "hey"
分配给 test
。它的值不会改变,除非你真的给 test
分配了别的东西。通过将其设为 @property
,每次访问 .test
时都会调用函数 def test
,因此可以动态计算其值;在本例中,基于 potato
的值。子类声明了它自己的 potato
,它隐藏了父类的 potato
属性.
正如您所看到的那样,您对 "surprising" 结果进行了大量实验,使用简单的东西,例如将列表的第一个元素分配给变量,以释放您对问题的错误概念。
为什么你认为你有 "nailed" 它并且当你超越简单时就做对了,并且 "find out" 你可以 "change a value" 使用元 class?
不 - 只是继续你之前的趋势,你仍然有错误的假设。
metaclass__new__
方法中的代码,就像classbody中的代码是运行创建每个 class 时一次。变量被创建、分配,之后就不再更改。
您的 "metaclass experiment" 通过在 new class 上设置然后为 potato
和 test
属性创建一个新值在创建这个新的 class (C) 时。
"meta" 与以下操作没有什么不同:
class B:
potato = 0
class C(B):
pass
C.potato = 1
print(C.potato) # Hey it works!
如果您想要 "magic changing values",表现得像计算值在电子表格程序中所做的那样,Python 有一种机制可以做到这一点,并且它与 metaclasses 无关:这些是 "descriptors"。其中最著名的是使用内置 property
.
创建的
因此,描述符会按照您在探索中尝试的方式进行操作:它们在读取或写入时看起来像一个普通属性,但可以通过任意数量的代码触发和计算 "behind the scenes"。它们确实是一种可用于实现 "reactive programing".
多个方面的机制
但是因为它们是用来使用的,而不是玩弄的,最流行的形式,property
被设计用于 class 个实例——而不是愚蠢地使用 class es 只是命名空间。
所以,我建议你切换到那个,你将有更多的探索要做:
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)
在深入研究元classes 之前,我试图理解Python classes。我遇到了一些我无法理解的代码。在这种情况下,classes 没有使用 self 而是 class 命名空间(没有使用 self 的选项,因此这里有问题)。我如何在一个 class 中定义一些命名空间变量,然后在子 class 中覆盖它们都依赖的值?
第一个案例
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)
它打印 hey。我理解是因为 test 指向字符串 "hey",它是不可变的。更改 B.potato = "not hey"
只会将 class 命名空间 potato 更改为新字符串,但不会更改 test 指向的内容至。所以我想,嘿,如果我用一个列表来做,那是通过引用吗?
class B():
potato = ["hey"]
test = potato[0]
class c(B):
B.potato[0] = "not hey"
c_potato = c().test
print(c_potato)
在我看来,这应该有效。我没有更改 potato 指向的内容,而是更改了值。不?但我知道它 wont' 实际上有效,因为 test 指向 potato[0] 而不是只是 土豆。所以是的,我明白为什么这也会打印 hey.
然后我意识到,如果 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() 的结果,所以没关系,仍然打印出 "hey"。
然后我想,metaclasses 可以解决这个问题吗?显然是的,它可以。
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)
并且正确打印 super hey fo b 和 not hey for c。问题是,这可以在没有 metaclasses 的情况下完成吗?脑袋都疼了
你可能想要 @property
:
class B:
potato = 'hey'
@property
def test(self):
return self.potato
class C(B):
potato = 'not hey'
您所做的是以某种方式将 "hey"
分配给 test
。它的值不会改变,除非你真的给 test
分配了别的东西。通过将其设为 @property
,每次访问 .test
时都会调用函数 def test
,因此可以动态计算其值;在本例中,基于 potato
的值。子类声明了它自己的 potato
,它隐藏了父类的 potato
属性.
正如您所看到的那样,您对 "surprising" 结果进行了大量实验,使用简单的东西,例如将列表的第一个元素分配给变量,以释放您对问题的错误概念。
为什么你认为你有 "nailed" 它并且当你超越简单时就做对了,并且 "find out" 你可以 "change a value" 使用元 class?
不 - 只是继续你之前的趋势,你仍然有错误的假设。
metaclass__new__
方法中的代码,就像classbody中的代码是运行创建每个 class 时一次。变量被创建、分配,之后就不再更改。
您的 "metaclass experiment" 通过在 new class 上设置然后为 potato
和 test
属性创建一个新值在创建这个新的 class (C) 时。
"meta" 与以下操作没有什么不同:
class B:
potato = 0
class C(B):
pass
C.potato = 1
print(C.potato) # Hey it works!
如果您想要 "magic changing values",表现得像计算值在电子表格程序中所做的那样,Python 有一种机制可以做到这一点,并且它与 metaclasses 无关:这些是 "descriptors"。其中最著名的是使用内置 property
.
因此,描述符会按照您在探索中尝试的方式进行操作:它们在读取或写入时看起来像一个普通属性,但可以通过任意数量的代码触发和计算 "behind the scenes"。它们确实是一种可用于实现 "reactive programing".
多个方面的机制但是因为它们是用来使用的,而不是玩弄的,最流行的形式,property
被设计用于 class 个实例——而不是愚蠢地使用 class es 只是命名空间。
所以,我建议你切换到那个,你将有更多的探索要做:
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)