Fun With *args and **kwargs
I once pair-programmed with a smart Pythonista who would exclaim “argh!” and “kwargh!” every time he typed out a function definition with optional or keyword parameters. We got along great otherwise. I guess that’s what programming in academia does to people eventually.
Now, while easily mocked, *args and **kwargs parameters are nevertheless a highly useful feature in Python. And understanding their potency will make you a more effective developer.
單星和雙星雖然被大家嘲笑,但是是python里面很有用的語言特性。理解它們的效力會使工作更有效率鹃祖。
So what are *args and **kwargs parameters used for? They allow a function to accept optional arguments, so you can create flexible APIs in your modules and classes:
def foo(required, *args, **kwargs):
print(required)
if args:
print(args)
if kwargs:
print(kwargs)
單星和雙星語法可以使得函數(shù)可以接受更加可選的參數(shù),所以我們可以在類或者魔魁阿忠創(chuàng)造更加靈活地API响驴。
The above function requires at least one argument called “required,” but it can accept extra positional and keyword arguments as well.
上面的函數(shù)至少要接受一個叫required的參數(shù),但是它也可以接受其他的位置參數(shù)或者關(guān)鍵字參數(shù)。
If we call the function with additional arguments, args will collect extra positional arguments as a tuple because the parameter name has a * prefix.
如果我們用更多的參數(shù)來調(diào)用這個函數(shù),函數(shù)可以以元組的形式采集其余的位置參數(shù)苔悦,因為參數(shù)名前面有單星前綴。
Likewise, kwargs will collect extra keyword arguments as a dictionary because the parameter name has a ** prefix.
相似得映砖,因為有雙星號前綴存在间坐,函數(shù)會以字典的形式手機其他的關(guān)鍵字參數(shù)。
Both args and kwargs can be empty if no extra arguments are passed to the function.
如果沒有其他的值傳入邑退,args和kwargs可以為空。
As we call the function with various combinations of arguments, you’ll see how Python collects them inside the args and kwargs parameters according to whether they’re positional or keyword arguments:
>>> foo()
TypeError:
"foo() missing 1 required positional arg: 'required'"
>>> foo('hello')
hello
>>> foo('hello', 1, 2, 3)
hello
(1, 2, 3)
>>> foo('hello', 1, 2, 3, key1='value', key2=999)
hello
(1, 2, 3)
{'key1': 'value', 'key2': 999}
python會根據(jù)是否是關(guān)鍵字參數(shù)還是位置參數(shù)來進行收集劳澄。
I want to make it clear that calling the parameters args and kwargs is simply a naming convention. The previous example would work just as well if you called them parms and argv. The actual syntax is just the asterisk () or double asterisk (), respectively.
這種語法表達里面最重要的是雙星號和單星號地技,而不是星號之后的命名。
However, I recommend that you stick with the accepted naming convention to avoid confusion. (And to get a chance to yell “argh!” and “kwargh!” every once in a while.)
作者還是在這里推薦我們按照慣例走秒拔。
Forwarding Optional or Keyword Arguments
It’s possible to pass optional or keyword parameters from one function to another. You can do so by using the argument-unpacking operators * and ** when calling the function you want to forward arguments to.
This also gives you an opportunity to modify the arguments before you pass them along. Here’s an example:
def foo(x, *args, **kwargs):
kwargs['name'] = 'Alice'
new_args = args + ('extra', )
bar(x, *new_args, **kwargs)
我們擁有著在傳遞雙星和單星參數(shù)之前對參數(shù)進行修改的機會莫矗。
This technique can be useful for subclassing and writing wrapper functions. For example, you can use it to extend the behavior of a parent class without having to replicate the full signature of its constructor in the child class. This can be quite convenient if you’re working with an API that might change outside of your control:
class Car:
def __init__(self, color, mileage):
self.color = color
self.mileage = mileage
class AlwaysBlueCar(Car):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.color = 'blue'
>>> AlwaysBlueCar('green', 48392).color
'blue'
利用這個性能我們就可以向上面那個例子中的一樣,我們不用太在意父類中初始化的參數(shù)有什么砂缩,直接一股腦扔個子類就好了作谚。但是我們也可以用過對子類的初始化設(shè)定對一些參數(shù)進行調(diào)整,而改變子類的屬性或者行為庵芭。
The AlwaysBlueCar constructor simply passes on all arguments to its parent class and then overrides an internal attribute. This means if the parent class constructor changes, there’s a good chance that AlwaysBlueCar would still function as intended.
父類發(fā)生變化妹懒,子類不變。
The downside here is that the AlwaysBlueCar constructor now has a rather unhelpful signature—we don’t know what arguments it expects without looking up the parent class.
但是有一個壞處就是我們?nèi)绻豢锤割愑质裁磪?shù)的話双吆,不知道子類都被傳遞了什么眨唬。
Typically you wouldn’t use this technique with your own class hierarchies. The more likely scenario would be that you’ll want to modify or override behavior in some external class which you don’t control.
一般情況下,我們不會用這個(就是雙星和單星)在我們自己的類的繼承上好乐。更可能的情景是你想去修改或者改寫一些你不控制的外部類的行為
But this is always dangerous territory, so best be careful (or you might soon have yet another reason to scream “argh!”).
在使用雙星或者單星語法的時候我們需要加以注意匾竿。
One more scenario where this technique is potentially helpful is writing wrapper functions such as decorators. There you typically also want to accept arbitrary arguments to be passed through to the wrapped function.
這個技術(shù)有用的一個場景是寫如裝飾器這樣的封裝函數(shù)的時候。我們可能會需要將接受任意的參數(shù)傳遞到被封裝的函數(shù)中蔚万。
And, if we can do it without having to copy and paste the original function’s signature, that might be more maintainable:
def trace(f):
@functools.wraps(f)
def decorated_function(*args, **kwargs):
print(f, args, kwargs)
result = f(*args, **kwargs)
print(result)
return decorated_function
@trace
def greet(greeting, name):
return '{}, {}!'.format(greeting, name)
>>> greet('Hello', 'Bob')
<function greet at 0x1031c9158> ('Hello', 'Bob') {}
'Hello, Bob!'
我們不用復(fù)制和粘貼原始函數(shù)的簽名就可以做到這一點岭妖,這樣更可維護。
With techniques like this one, it’s sometimes difficult to balance the idea of making your code explicit enough and yet adhere to the Don’t Repeat Yourself (DRY) principle. This will always be a tough choice to make. If you can get a second opinion from a colleague, I’d encourage you to ask for one.
需要平衡在使得代碼足夠清晰和不要自己重復(fù)自己(應(yīng)該是大量重復(fù)的代碼)。
Key Takeaways
- *args and **kwargs let you write functions with a variable number of arguments in Python.
- *args collects extra positional arguments as a tuple **kwargs collects the extra keyword arguments as a dictionary.
- The actual syntax is * and **. Calling them args and kwargs is just a convention (and one you should stick to).