【发布时间】:2018-06-30 01:10:32
【问题描述】:
具体来说,使用类属性创建更多类属性,然后删除原始属性是否可以接受/良好做法?
假设我有一个超类。由此,我派生了许多子类,但所有这些都需要定义一些类属性。如果我正常执行此操作,它看起来像:
class Bar(Foo):
someval = 0
someotherval = ('a', 'b', 'c')
importantval = [1, 2, 3]
thisval = 'this'
names = ['John', 'Joe']
每个子类都需要定义所有这些属性。但是,如果我们以某种方式使用一个变量来创建所有这些变量,它会看起来像:
class Bar(Foo):
classvars = (0, ('a', 'b', 'c'), [1, 2, 3], 'this', ['John', 'Joe'])
然后,在创建类之后,它在内部看起来与我们分别定义所有属性的类的版本相同。
看,我之前尝试过只使用没有元类的超类来做到这一点,例如:
class Foo:
@classmethod
def __init__(cls, val):
cls.val = val
class Bar(Foo):
Foo.__init__(5)
但是如果你知道@classmethod 是如何工作的,那么你就会知道cls 最终会成为一个类Foo 引用,而不是一个类Bar 引用(在Bar 的创建过程中调用时,并且cls 仅在 Bar 实例创建期间成为 Bar 引用)。然后我也尝试改用@staticmethod,但是据我所知,在创建类时,您不能引用在方法定义之外创建的类。
即
class Bar(Foo):
Foo.__init__(Bar, 5)
这会引发 NameError(如果 Foo 的 __init__ 方法是静态方法)。
最终,我了解了元类,并发现我可以做到这一点:
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 的类属性,它会使用它来创建其他类属性,然后删除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']
我的主要问题是:以这种方式使用元类是否可以接受(或者由于某种原因通常会避免),尤其是如果您已经出于充分的理由使用元类?
附带的问题是:没有元类有没有办法做到这一点?
【问题讨论】:
-
我不会那样做,只是因为“显式比隐式好。”
-
@cco 我知道这与编码风格本身有关,但是如果您明确(通过代码/文档中的 cmets)创建
Bar的两种风格是相同的,那只是那个更快,更有效。如果您有 10 个需要定义的不同类变量和 25 个需要创建的不同子类,那么您至少需要 275 行正常执行此操作,而短途只需 50 行。 -
我会给你更短的时间,但我怀疑它是否更快(运行,而不是编写)或更高效。对我来说,名称与值的分离是一个重大损失。不同意见同样有效。关于另一种方法的问题,您可以使用类装饰器,这将使
_superclass舞蹈变得不必要,并使魔法不那么隐藏(一件好事,恕我直言)。 -
您是否在运行时创建类?你在自动化模块创建吗?您要解决什么问题?- 出于您的目的,为什么具有单个
classvars属性的子类定义更好 比具有所有类属性的子类定义写出? -
@cco 我的意思是,就编写代码而言,它会更有效率:而不是花 20 分钟来编写所有子类,它可能只需要 2 分钟。我什至不知道我是怎么做到的会使用类装饰器来做到这一点,但我确实知道如何使用元类来做到这一点(这实际上很容易),这就是我这样做的原因。
标签: python python-3.x class inheritance metaclass