在 Python 中,是否可以接受使用元 class 添加和删除许多 class 属性?
In Python, is it acceptable to use a metaclass to add and remove many class attributes?
具体来说,是acceptable/good的做法是使用一个class属性创建更多的class属性,然后删除原来的?
说我有一个超级class。由此,我派生出许多子classes,但所有这些都需要定义一些class 属性。如果我正常执行此操作,它将看起来像:
class Bar(Foo):
someval = 0
someotherval = ('a', 'b', 'c')
importantval = [1, 2, 3]
thisval = 'this'
names = ['John', 'Joe']
每个子class 都需要定义所有这些属性。但是,如果我们以某种方式使用一个变量来创建所有这些,它将看起来像:
class Bar(Foo):
classvars = (0, ('a', 'b', 'c'), [1, 2, 3], 'this', ['John', 'Joe'])
然后,在创建 class 之后,它在内部看起来与我们单独定义所有属性的 class 版本相同。
看,我之前尝试通过仅使用 superclass 而没有 metaclass 来做到这一点,例如:
class Foo:
@classmethod
def __init__(cls, val):
cls.val = val
class Bar(Foo):
Foo.__init__(5)
但是如果您知道 @classmethod
是如何工作的,那么您就会知道 cls
最终会成为 class Foo
引用,而不是 class Bar
引用(在 Bar
创建期间调用时,cls
最终仅在 Bar
实例创建期间成为 Bar
引用)。然后我也尝试使用 @staticmethod
代替,但是据我所知,在创建 class 时,您不能引用在方法定义之外创建的 class。
即
class Bar(Foo):
Foo.__init__(Bar, 5)
这会引发 NameError(如果 Foo
的 __init__
方法是静态方法)。
最终,我了解了 metaclasses,我发现我可以做到这一点:
class MetaFoo(type):
def __init__(cls, name, bases, namespace):
super(MetaFoo, cls).__init__(name, bases, namespace)
if '_superclass' not in namespace: # A special attribute to distinguish between classes that we want to affect and those that we don't
attrnames = ('someval', 'someotherval', 'importantval', 'thisval', 'names')
if 'classvars' in namespace:
for attr, attrname in zip(getattr(cls, 'classvars'), attrnames):
setattr(cls, attrname, attr)
namespace[attrname] = attr
delattr(cls, 'classvars')
else: # Allow the definitions to be separate, instead of through "classvars", but also make sure that all the required attributes are defined
for attrname in attrnames:
if attrname not in namespace:
raise AttributeError('"%s" not found.' % attrname)
class Foo(metaclass=MetaFoo):
_superclass = True # The value of this attribute doesn't matter
def __init__(self, value):
self.value = value
def dosomething(self):
return self.value * self.someval # Can use type(self).someval for the class attribute, as long as the class itself is never passed as self
class Bar(Foo):
classvars = (5, ('c', 'b', 'a'), [3, 2, 1], 'This', ['Jimmy', 'Bob'])
上面所做的,简而言之,如果它找到一个名为 classvars
的 class 属性,它会使用它来创建其他 class 属性,然后它会删除 classvars
.在这个例子中,做:
class Bar(Foo):
classvars = (5, ('c', 'b', 'a'), [3, 2, 1], 'This', ['Jimmy', 'Bob'])
给你一个相同的结果:
class Bar(Foo):
someval = 5
someotherval = ('c', 'b', 'a')
importantval = [3, 2, 1]
thisval = 'This'
names = ['Jimmy', 'Bob']
我的主要问题是:以这种方式使用 metaclass 是否可以接受(或者出于某种原因通常会避免),特别是如果您已经在使用 metaclass有充分的理由吗?
附带的问题是:没有元class有什么方法可以做到这一点吗?
是的,你可以做到。但我不知道它是否比复制+粘贴所有需要的属性更好。
如果您要这样做,为了让查看您的子class之一的代码的人保持最低限度的可读性 - 包括从现在起的一周内的您 - 您至少应该使用字典而不是序列。这样,在查看子 class.
时,您将始终知道哪个值映射到每个属性
此外,请记住,如果至少某些属性具有对许多子classes 有效的良好默认值,则继承机制可以很好地保留这些默认值,同时允许您只明确声明每个子 class 真正需要不同的属性(并且输入比使用字典更简单,这至少需要在名称周围加上额外的引号)。
否则,就语言而言,还可以。您宁愿在基础 class 上有一个特殊的属性名称,允许您在基础 class 主体上输入 required/desired 属性名称,而不是将它们硬编码在元 [=26] 上=]:看起来真的 "uncivilized"。通过在 baseclass 上添加一个属性,metaclass 可以在不同的对象层次结构中重复使用。
(只需使用 for
将 bases
参数迭代到元 class 以检查是否存在具有属性名称的参数)
啊,因为它是问题的一部分:关于删除 class 创建 class 后不再有用的属性,使用 metaclass - 完全可以.
具体来说,是acceptable/good的做法是使用一个class属性创建更多的class属性,然后删除原来的?
说我有一个超级class。由此,我派生出许多子classes,但所有这些都需要定义一些class 属性。如果我正常执行此操作,它将看起来像:
class Bar(Foo):
someval = 0
someotherval = ('a', 'b', 'c')
importantval = [1, 2, 3]
thisval = 'this'
names = ['John', 'Joe']
每个子class 都需要定义所有这些属性。但是,如果我们以某种方式使用一个变量来创建所有这些,它将看起来像:
class Bar(Foo):
classvars = (0, ('a', 'b', 'c'), [1, 2, 3], 'this', ['John', 'Joe'])
然后,在创建 class 之后,它在内部看起来与我们单独定义所有属性的 class 版本相同。
看,我之前尝试通过仅使用 superclass 而没有 metaclass 来做到这一点,例如:
class Foo:
@classmethod
def __init__(cls, val):
cls.val = val
class Bar(Foo):
Foo.__init__(5)
但是如果您知道 @classmethod
是如何工作的,那么您就会知道 cls
最终会成为 class Foo
引用,而不是 class Bar
引用(在 Bar
创建期间调用时,cls
最终仅在 Bar
实例创建期间成为 Bar
引用)。然后我也尝试使用 @staticmethod
代替,但是据我所知,在创建 class 时,您不能引用在方法定义之外创建的 class。
即
class Bar(Foo):
Foo.__init__(Bar, 5)
这会引发 NameError(如果 Foo
的 __init__
方法是静态方法)。
最终,我了解了 metaclasses,我发现我可以做到这一点:
class MetaFoo(type):
def __init__(cls, name, bases, namespace):
super(MetaFoo, cls).__init__(name, bases, namespace)
if '_superclass' not in namespace: # A special attribute to distinguish between classes that we want to affect and those that we don't
attrnames = ('someval', 'someotherval', 'importantval', 'thisval', 'names')
if 'classvars' in namespace:
for attr, attrname in zip(getattr(cls, 'classvars'), attrnames):
setattr(cls, attrname, attr)
namespace[attrname] = attr
delattr(cls, 'classvars')
else: # Allow the definitions to be separate, instead of through "classvars", but also make sure that all the required attributes are defined
for attrname in attrnames:
if attrname not in namespace:
raise AttributeError('"%s" not found.' % attrname)
class Foo(metaclass=MetaFoo):
_superclass = True # The value of this attribute doesn't matter
def __init__(self, value):
self.value = value
def dosomething(self):
return self.value * self.someval # Can use type(self).someval for the class attribute, as long as the class itself is never passed as self
class Bar(Foo):
classvars = (5, ('c', 'b', 'a'), [3, 2, 1], 'This', ['Jimmy', 'Bob'])
上面所做的,简而言之,如果它找到一个名为 classvars
的 class 属性,它会使用它来创建其他 class 属性,然后它会删除 classvars
.在这个例子中,做:
class Bar(Foo):
classvars = (5, ('c', 'b', 'a'), [3, 2, 1], 'This', ['Jimmy', 'Bob'])
给你一个相同的结果:
class Bar(Foo):
someval = 5
someotherval = ('c', 'b', 'a')
importantval = [3, 2, 1]
thisval = 'This'
names = ['Jimmy', 'Bob']
我的主要问题是:以这种方式使用 metaclass 是否可以接受(或者出于某种原因通常会避免),特别是如果您已经在使用 metaclass有充分的理由吗?
附带的问题是:没有元class有什么方法可以做到这一点吗?
是的,你可以做到。但我不知道它是否比复制+粘贴所有需要的属性更好。
如果您要这样做,为了让查看您的子class之一的代码的人保持最低限度的可读性 - 包括从现在起的一周内的您 - 您至少应该使用字典而不是序列。这样,在查看子 class.
时,您将始终知道哪个值映射到每个属性此外,请记住,如果至少某些属性具有对许多子classes 有效的良好默认值,则继承机制可以很好地保留这些默认值,同时允许您只明确声明每个子 class 真正需要不同的属性(并且输入比使用字典更简单,这至少需要在名称周围加上额外的引号)。
否则,就语言而言,还可以。您宁愿在基础 class 上有一个特殊的属性名称,允许您在基础 class 主体上输入 required/desired 属性名称,而不是将它们硬编码在元 [=26] 上=]:看起来真的 "uncivilized"。通过在 baseclass 上添加一个属性,metaclass 可以在不同的对象层次结构中重复使用。
(只需使用 for
将 bases
参数迭代到元 class 以检查是否存在具有属性名称的参数)
啊,因为它是问题的一部分:关于删除 class 创建 class 后不再有用的属性,使用 metaclass - 完全可以.