任何理由不总是使用关键字参数?
在跳入python之前,我已经开始使用一些Objective-C / Cocoa书籍。 我记得,大多数函数都需要明确声明关键字参数。 直到最近,我忘记了这一切,只是在Python中使用位置参数。 但是最近我遇到了一些由于不正当的位置而造成的一些错误 – 他们是那些偷偷摸摸的小东西。
让我思考 – 一般来说,除非有特别要求非关键字参数的情况 – 有什么好的理由不使用关键字参数? 总是使用它们被认为是不好的风格,即使是简单的function?
我觉得我的50行程序中的大部分已经定期扩展到500或更多行,如果我习惯于总是使用关键字参数,随着代码的增长,代码将变得更容易阅读和维护。 任何理由,这可能不是这样吗?
更新:
我所得到的一般印象是,它的风格偏好,有许多好的论点,一般不应该用于非常简单的论证,而是与其他风格保持一致。 在接受之前,我只想澄清一下 – 这种方法是否存在任何特定的非风格问题 – 例如,性能明显下降?
除了代码的清晰性和可读性之外,没有任何理由不使用关键字参数。 是否使用关键字的select应该基于关键字是否在读取代码时添加额外的有用信息。
我遵循以下一般规则:
- 如果很难从函数名称中推断参数的函数(名称) – 通过关键字传递它(例如,我不想在我的代码中有
text.splitlines(True)
)。 - 如果很难推断参数的顺序,例如,如果你有太多的参数,或者你有独立的可选参数 – 通过关键字传递(例如
funkyplot(x, y, None, None, None, None, None, None, 'red')
看起来不是特别好)。 - 如果参数的目的是显而易见的,不要通过关键字传递前几个参数。 你看,
sin(2*pi)
比sin(value=2*pi)
,对于plot(x, y, z)
也是如此。
在大多数情况下,稳定的强制性参数是位置的,可选参数是关键字。
在性能上也可能有所不同,因为在每个实现中关键字的参数会稍微慢一点,但是考虑到这通常是一个不成熟的优化,而且结果不是很重要,所以我认为这对决定是不重要的。
更新:非文体问题
关键字参数可以做所有的位置参数,如果你定义了一个新的API,除了可能的性能问题之外,没有任何技术上的缺点。 但是,如果将代码与现有元素结合使用,则可能会遇到小问题。
考虑以下:
- 如果你使你的函数带有关键字参数,那么它就成为你的接口的一部分。 对于相同的参数,您不能用另一个具有类似签名但不同关键字的函数replace您的函数。
- 你可能想在你的函数上使用装饰器或其他工具,假定你的函数有一个位置参数。 未绑定的方法就是这种实用工具的一个例子,因为它们总是将第一个参数作为位置传递给位置,所以即使在定义中有
self
variables,cls.method(self=cls_instance)
也不起作用。
如果你devise好你的API并logging关键字参数的使用,那么这些都不是真正的问题,特别是如果你没有devise一些应该与已经存在的东西互换的东西。
如果您的考虑是为了提高函数调用的可读性,为什么不简单地声明函数是正常的,
def test(x, y): print "x:", x print "y:", y
只需通过显式声明名称来调用函数,如下所示:
test(y=4, x=1)
这显然给你的输出:
x: 1 y: 4
或者这个练习是毫无意义的。
这样可以避免参数是可选的,并且需要默认值(除非您希望它们是,在这种情况下,只要继续使用关键字参数!),并为您提供所有通用性,并提高命名参数的可读性,而不受命令的限制。
那么,有几个原因我不这样做。
如果你所有的参数都是关键字参数,它会增加代码中的噪声,并且可能会清除哪些参数是必需的,哪些参数是可选的。
另外,如果我必须使用你的代码,我可能想杀了你! (开玩笑),但必须input所有参数的名称每次…不那么有趣。
我记得在UNIX程序中阅读了一个关于“选项”的非常好的解释:“选项是可选的,一个程序应该可以运行而不用任何选项”。
Python中的关键字参数也可以应用相同的原则。 这些参数应该允许用户“定制”函数调用,但是一个函数应该可以被调用,而不需要任何隐式的关键字 – 值参数对。
只是为了提供一个不同的论点,我认为有些情况下命名参数可能会提高可读性。 例如,想象一个在您的系统中创build用户的函数:
create_user("George", "Martin", "gm@example.com", "payments@example.com", "1", "Radius Circle")
从这个定义来看,这些数值可能意味着什么,尽pipe它们都是必需的,但是对于命名参数来说,它总是显而易见的:
create_user( first_name="George", last_name="Martin", contact_email="gm@example.com", billing_email="payments@example.com", street_number="1", street_name="Radius Circle")
当Python的内置的compile()
和__import__()
函数获得关键字参数支持时 ,为了清晰起见,也提出了相同的论点。 如果有的话,似乎没有显着的性能影响。
现在,如果你让你的函数只接受关键字参数(而不是在调用它们时使用关键字传递位置参数,这是允许的),那么是的,这将是恼人的。
有时候,事情应该简单,因为它们很简单。
如果您始终强制您在每个函数调用中使用关键字参数,那么您的代码很快就不可读。
当参数的含义很明显时,我没有看到使用关键字参数的目的
我可以看到的一个缺点是,你不得不为所有事情设想一个合理的默认值,在很多情况下可能没有任何合理的默认值(包括None
)。 那么你会觉得有必要写一大堆的error handling代码,在逻辑上应该是位置arg的kwarg没有被指定的情况下。
想象一下,每次写这样的东西..
def logarithm(x=None): if x is None: raise BadArgsException("You can't do log(None), silly!")
关键字参数很好,当你有很长的参数列表没有明确定义的顺序(你不能轻易想出一个清晰的计划来记住); 然而,在很多情况下,使用它们是过分的,或使程序不太清楚。
首先,有时关键字的顺序比关键字参数的名称更容易记住,指定参数的名称可能会使其不太清晰。 从以下文档string的scipy.random
采取randint
:
randint(low, high=None, size=None) Return random integers x such that low <= x < high. If high is None, then 0 <= x < low.
在我看来,当想从[0,10]中产生一个随机的int来randint(10)
不是randint(low=10)
。 如果你需要在[0,10)中生成一个有100个数字的数组,你可以记住参数的顺序并写入randint(0, 10, 100)
。 但是,您可能不记得variables名称(例如,是第一个参数low,lower,start,min,minimum),一旦您需要查找参数名称,您可能不会使用它们正确的顺序)。
还要考虑可变参数函数(可变参数个数本身是匿名的函数)。 例如,你可能想要写下如下的东西:
def square_sum(*params): sq_sum = 0 for p in params: sq_sum += p*p return sq_sum
可以应用一堆裸露的参数( square_sum(1,2,3,4,5) # gives 55
)。 当然你可以写一个函数来获取一个名为关键字的可迭代的def square_sum(params):
并称之为square_sum([1,2,3,4,5])
但这可能不那么直观,特别是当没有潜在的混淆关于参数名称或其内容。