在下面的代码中,如果存在
@classmethod
注释,则允许内部 def
new()
代替目标的
__new__()
-- 但该类会传递两次。如果
@classmethod
被删除,那么我们会收到类似“”的错误。
@classmethod
这里在做什么,有没有办法不用它? (我的动机是清晰的:我不理解的代码看起来像是等待发生的事故。)
"""Namedtuple annotation.
Creates a namedtuple out of a class, based on the signature of that class's
__init__ function. Defaults are respected. After namedtuple's initializer is
run, the original __init__ is run as well, allowing one to assign synthetic
parameters and internal book-keeping variables.
The class must not have varargs or keyword args.
"""
import collections
import inspect
def namedtuple(cls):
argspec = inspect.getargspec(cls.__init__)
assert argspec.varargs is None
assert argspec.keywords is None
non_self_args = argspec.args[1:]
# Now we can create the new class definition, based on a namedtuple.
bases = (collections.namedtuple(cls.__name__, non_self_args), cls)
namespace = {'__doc__': cls.__doc__}
newcls = type(cls.__name__, bases, namespace)
# Here we set up the new class's __new__, which hands off to namedtuple's
# after setting defaults.
@classmethod
def new(*args, **kwargs):
kls, _kls_again = args[:2] # The class is passed twice...?
# Resolve default assignments with this utility from inspect.
values = inspect.getcallargs(cls.__init__, None, *args[2:], **kwargs)
values = [values[_] for _ in non_self_args]
obj = super(newcls, kls).__new__(kls, *values)
cls.__init__(obj, *values) # Allow initialization to occur
return obj
# The @classmethod annotation is necessary because otherwise we get an
# error like "unbound method new takes a class instance".
newcls.__new__ = new
return newcls
代码片段的目的是创建一个装饰器,它接受一个类并返回该类的“命名元组化”版本。这意味着新类表现得像一个
namedtuple
,并使用给定类的
__init__
方法的签名作为其字段。
@classmethod
装饰器在这里是必不可少的,因为它允许
new
方法被调用为
newcls.__new__(newcls, ...)
, 而不是
newcls.__new__(cls, ...)
。让我解释一下原因:
-
__new__
方法: 在 Python 中,__new__
方法负责创建一个新实例并返回它。它是类的静态方法,它接收该类本身作为第一个参数(通常命名为cls
)。 -
classmethod
装饰器: 此装饰器用于创建接受类本身作为第一个参数的方法,而不是类的实例。它是 Python 中的一种元编程方法,可以操作类本身。 -
在本例中:
new
方法旨在替换由namedtuple
装饰器创建的newcls
的__new__
方法。 -
newcls
是新的“命名元组化”类。 -
new
方法需要接收newcls
作为第一个参数,以正确地实例化它。
如果删除
@classmethod
,会发生什么?
如果没有
@classmethod
装饰器,
new
方法将成为一个普通的实例方法。这意味着它期望一个类的实例作为第一个参数(
self
)。当尝试使用
newcls.__new__(newcls, ...)
调用它时,Python 解释器会将
newcls
作为
self
参数传递,从而传递该类
两次
。这将导致错误“未绑定方法
new
需要一个类实例”,因为该方法期望一个实例,但接收到了一个类。
没有
@classmethod
可以吗?
从技术上讲,可以,但代码将不那么优雅,并且需要解决方法。以下是可能的解决方法:
def new(*args, **kwargs):
kls = args[0] # 获取第一个参数,即类本身
_kls_again = args[1]
# ...(其余代码相同)
newcls.__new__ = classmethod(new) # 将 new 明确绑定为类方法
在此解决方法中,我们手动将
new
绑定为类方法,使用
classmethod(new)
。这在功能上与使用
@classmethod
装饰器相同,但代码的意图可能不太清晰。
结论
@classmethod
装饰器在此代码片段中是
必不可少的
,因为它可以确保
new
方法接收类本身作为第一个参数,从而允许它正确地替代
__new__
方法。删除它需要解决方法,这可能会降低代码的可读性和可维护性。