在元类中传递父属性
Pass parent attribute in metaclass
metaclass定义如下:
class MyMeta(type):
def __new__(metacls, name, bases, namespace, **kwargs):
# uses attribute a,b,c,d
a = namespace["a"]
b = namespace["b"]
c = namespace["c"]
d = namespace["d"]
...
return super().__new__(metacls, name, bases, namespace, **kwargs)
此元class 使用属性 a
、b
、c
、d
。
现在 classes 可以用这个 metaclass
创建为:
class A(metaclass=MyMeta):
a = "etc/dev"
b = 2
c = "c"
d = "d"
class B(metaclass=MyMeta):
a = "etc/dev/null"
b = 2
c = "c"
d = "d"
每个 class 的属性 a
是可变的。
我想提取基础 class 中的常量属性以避免重复:
class BaseClass:
b = 2
c = "c"
d = "d"
并使用 metaclass 创建 classes:
class A(BaseClass, metaclass=MyMeta):
a = "/etc/dev"
class B(BaseClass, metaclass=MyMeta):
a = "/etc/dev/null"
元class 抱怨它无法从父 class.
中找到属性 b
test_mode = namespace["b"]
KeyError: 'b'
有没有办法将属性从父元传递给子元class?
限制条件: 无法更新 MyMeta
metaclass,因为它已经 borrowed/imported它来自现有的图书馆。
您可以通过使元class __new__()
方法也 检查所有基 classes 的命名空间class 正在定义中。这是一种方法。请注意,它会过滤掉名称空间中具有 dunder 标识符的属性。
由于您无法更改元数据 class 您“借用”了一个自定义元数据。我之前回答的这个修订版还使用 collection.ChainMap
class 来考虑所有名称空间,而无需实际组合它们。
from collections import ChainMap
import re
class BorrowedMeta(type):
def __new__(metacls, name, bases, namespace, **kwargs):
# uses attribute a,b,c,d
a = namespace["a"]
b = namespace["b"]
c = namespace["c"]
d = namespace["d"]
...
return super().__new__(metacls, name, bases, namespace, **kwargs)
def is_dunder(name):
"""Determine if name is a Python dunder identifier."""
return re.match(r'^__[^\d\W]\w*\Z__$', name, re.UNICODE)
class MyMeta(BorrowedMeta):
def __new__(metacls, name, bases, namespace, **kwargs):
chainmap = ChainMap(*[namespace] +
[{k: v for (k, v) in vars(base).items() if not(is_dunder(k))}
for base in bases])
return super().__new__(metacls, name, bases, {**chainmap}, **kwargs)
class BaseClass:
b = 2
c = "c"
d = "d"
class A(BaseClass, metaclass=MyMeta):
a = "/etc/dev"
class B(BaseClass, metaclass=MyMeta):
a = "/etc/dev/null"
from pprint import pprint
pprint(vars(A))
print()
pprint(vars(B))
输出:
mappingproxy({'__doc__': None,
'__module__': '__main__',
'a': '/etc/dev',
'b': 2,
'c': 'c',
'd': 'd'})
mappingproxy({'__doc__': None,
'__module__': '__main__',
'a': '/etc/dev/null',
'b': 2,
'c': 'c',
'd': 'd'})
不,如果不改变 metaclass 的行为,您将无法为所欲为。
问题是 MyMeta.__new__
正在查找的 namespace
参数 只包含 在 class
语句中定义的值新 class。它不包含任何继承的东西,因为 class 和它的基础还没有合并(这就是 MyMeta.__new__
在其实现底部调用 super().__new__
时所做的,在之后它已经用a
/b
/c
变量完成了它的事情。
metaclass定义如下:
class MyMeta(type):
def __new__(metacls, name, bases, namespace, **kwargs):
# uses attribute a,b,c,d
a = namespace["a"]
b = namespace["b"]
c = namespace["c"]
d = namespace["d"]
...
return super().__new__(metacls, name, bases, namespace, **kwargs)
此元class 使用属性 a
、b
、c
、d
。
现在 classes 可以用这个 metaclass
创建为:
class A(metaclass=MyMeta):
a = "etc/dev"
b = 2
c = "c"
d = "d"
class B(metaclass=MyMeta):
a = "etc/dev/null"
b = 2
c = "c"
d = "d"
每个 class 的属性 a
是可变的。
我想提取基础 class 中的常量属性以避免重复:
class BaseClass:
b = 2
c = "c"
d = "d"
并使用 metaclass 创建 classes:
class A(BaseClass, metaclass=MyMeta):
a = "/etc/dev"
class B(BaseClass, metaclass=MyMeta):
a = "/etc/dev/null"
元class 抱怨它无法从父 class.
中找到属性b
test_mode = namespace["b"]
KeyError: 'b'
有没有办法将属性从父元传递给子元class?
限制条件: 无法更新 MyMeta
metaclass,因为它已经 borrowed/imported它来自现有的图书馆。
您可以通过使元class __new__()
方法也 检查所有基 classes 的命名空间class 正在定义中。这是一种方法。请注意,它会过滤掉名称空间中具有 dunder 标识符的属性。
由于您无法更改元数据 class 您“借用”了一个自定义元数据。我之前回答的这个修订版还使用 collection.ChainMap
class 来考虑所有名称空间,而无需实际组合它们。
from collections import ChainMap
import re
class BorrowedMeta(type):
def __new__(metacls, name, bases, namespace, **kwargs):
# uses attribute a,b,c,d
a = namespace["a"]
b = namespace["b"]
c = namespace["c"]
d = namespace["d"]
...
return super().__new__(metacls, name, bases, namespace, **kwargs)
def is_dunder(name):
"""Determine if name is a Python dunder identifier."""
return re.match(r'^__[^\d\W]\w*\Z__$', name, re.UNICODE)
class MyMeta(BorrowedMeta):
def __new__(metacls, name, bases, namespace, **kwargs):
chainmap = ChainMap(*[namespace] +
[{k: v for (k, v) in vars(base).items() if not(is_dunder(k))}
for base in bases])
return super().__new__(metacls, name, bases, {**chainmap}, **kwargs)
class BaseClass:
b = 2
c = "c"
d = "d"
class A(BaseClass, metaclass=MyMeta):
a = "/etc/dev"
class B(BaseClass, metaclass=MyMeta):
a = "/etc/dev/null"
from pprint import pprint
pprint(vars(A))
print()
pprint(vars(B))
输出:
mappingproxy({'__doc__': None,
'__module__': '__main__',
'a': '/etc/dev',
'b': 2,
'c': 'c',
'd': 'd'})
mappingproxy({'__doc__': None,
'__module__': '__main__',
'a': '/etc/dev/null',
'b': 2,
'c': 'c',
'd': 'd'})
不,如果不改变 metaclass 的行为,您将无法为所欲为。
问题是 MyMeta.__new__
正在查找的 namespace
参数 只包含 在 class
语句中定义的值新 class。它不包含任何继承的东西,因为 class 和它的基础还没有合并(这就是 MyMeta.__new__
在其实现底部调用 super().__new__
时所做的,在之后它已经用a
/b
/c
变量完成了它的事情。