Python **pop(x)与将x定义为函数参数

Python **pop(x)与将x定义为函数参数,python,python-3.x,Python,Python 3.x,假设您正在编写一个子类,该子类具有一个构造函数,该构造函数将其未使用的KWARG传递给父构造函数,但您的类具有需要存储的参数x,该参数不应传递给父构造函数 我看到了两种不同的方法: def __init__(self, **kwargs): self.x = kwargs.pop('x', 'default') super().__init__(**kwargs) 及 这两个构造函数之间是否存在任何功能差异?有什么理由用一个代替另一个吗 我能看到的唯一区别是第二种形式,它在签名

假设您正在编写一个子类,该子类具有一个构造函数,该构造函数将其未使用的KWARG传递给父构造函数,但您的类具有需要存储的参数
x
,该参数不应传递给父构造函数

我看到了两种不同的方法:

def __init__(self, **kwargs):
    self.x = kwargs.pop('x', 'default')
    super().__init__(**kwargs)

这两个构造函数之间是否存在任何功能差异?有什么理由用一个代替另一个吗


我能看到的唯一区别是第二种形式,它在签名中定义了
x
,使用户可以更好地将其视为一个可能的参数,或者将其作为一个IDE提供为一个自动完成选项。或者在Python 3.5+中,您可以向
x
添加类型注释。这会使第一种形式客观上更糟糕吗?

除了代码清晰度上的明显差异外,调用函数的速度可能会有一些差异,在本例中,方法init()

如果可以的话,在两种方法中都用默认值定义所有必要的参数(如果有的话),并按经典方式传递它们,排除不希望的参数。 通过这种方式,代码变得清晰,调用速度保持不变

如果您需要一些微优化,那么使用timeit来检查哪些工作更快。 我希望加上“x”作为论据的人可能是赢家。 因为直接从局部变量获取其值会更快,而kwargs dict()则更小

当您使用“普通”参数时,它们会自动插入到函数局部变量字典中

使用*args和/或**kwargs时,它们是作为新局部变量添加的附加tuple()和/或dict()。它们首先是从传入函数调用的参数创建的。 当您将它们传递给下一个函数时,它们将被提取 以匹配该函数的调用签名。在这两种操作中,您都会损失一点点速度。 如果从kwargs字典中添加或删除某些内容(x=kwargs.pop(“x”),也会损失一些速度

通过观察这两种代码,似乎它们的通话速度是相等的。但是你应该检查一下。如果在初始化实例时不需要额外的0.000001秒,那么这两个选项都可以,只需选择您最喜欢的


但是,同样,如果您可以自由地这样做,并且不会严重影响代码的维护,请定义所有参数及其默认值并逐个传递。

正如Giacomo Alzetta在评论中已经提到的,第二个版本允许将
x
作为位置参数传递,而第一个版本只允许命名参数,在第二个表单中,您可以同时使用
Child(x=2)
Child(2)
,而第一个表单只支持
Child(x=2)

另外,当使用检查检查方法的签名时,第二个表单将清楚地提到
x
param的存在,而第一个表单不会

最后,如果未通过
x
,第二个版本将产生一个稍微清晰的异常

这就是功能上的差异

有什么理由用一个代替另一个吗

嗯。。。作为一般规则,尽可能使用显式参数更为简洁(最佳实践),即使只是为了可读性,根据经验,它通常确实使维护更容易。因此,从这个角度来看,第二种形式可以被视为比第一种形式“客观上更好”

这就是说,当父方法有几十个基本上是可选的、很少使用的参数(
django.forms.Form
,我在看你)并且你还想保留位置参数顺序时,只需为子方法使用泛型的
*args、**kwargs
签名并强制附加参数就可以了以kwargs的形式通过。假设您在docstring中清楚地记录了这一点,它仍然足够明确(就我而言,YMMV),并且还避免了大量的混乱(您可以查看
django.forms.Form
,了解我这里的具体含义)

因此,与“最佳实践”和其他黄金法则一样,你必须理解并权衡当前具体案例的利弊


PS:我想说清楚,django的
表单
类签名非常有意义,所以我不在此赘述-这只是问题没有“漂亮”解决方案的一种情况,句号。

前者不允许位置参数,后者允许。那么像
def\u init一样,只允许关键字参数呢__(self,*,x='default',**kwargs)
?@GiacomoAlzetta两种形式都允许位置参数,我不明白为什么它们中的任何一种都不允许。@michcio1234前者将提出
接受0个位置参数,但N被给出了
错误…(在这种情况下打折
self
)@deceze啊,现在我明白了-我以为giacomo alzetta的意思是“不允许在函数签名中定义位置参数”,而他的意思是“不允许使用位置参数调用函数”。
def __init__(self, x='default', **kwargs):
    self.x = x
    super().__init__(**kwargs)