共计 2375 个字符,预计需要花费 6 分钟才能阅读完成。
可接受任意数量参数的函数
在 Python 中定义函数非常简单,例如一个计算二次幂的函数:
def power(x):
return x * x
如果我们想要一个可以 接受任意数量参数 的函数,该怎么定义呢?
比如说想要一个函数,接受一组数据,并计算它们的平方值的和。
当然可以直接传递列表或者元组做函数的参数:
def sum_power(x):
sum = 0
for i in x:
sum += i * i
return sum
但是这样做的话,如果想使用这个函数,必须先搞一个列表或元组出来,像这样调用:
sum_power([1,2,3,4])
使用下面这个方法就简单多了:
def sum_power(*x):
sum = 0
for i in x:
sum += i * i
return sum
使用 *
开头的参数,那么在这之后的位置参数都会被当作一个序列来处理。
但是如果 本身就有一个列表 arr = [1,2,3,4,5]
要怎么处理呢?
可以这样调用这个函数:
sum_power(*arr)
在调用函数时给列表之类的参数加一个 *,就相当于把这个列表拆开,所有元素做为函数的参数传递进去。
如果要使用任意数量的关键字参数,可以使用 **
开头的参数。
>>> def a(**attrs):
... print(attrs)
...
>>> a(name="张三", age="18")
{'name': '张三', 'age': '18'}
>>>
可以发现,以 **
开头的参数被包装成了字典来处理。
def a(*args, **kwargs):
pass
像这样的函数就能接受任意数量的位置参数和关键字参数了。
keyword-only 参数
在定义函数时,*
开头的参数必须是最后一个位置参数,而 **
开头的参数则必须是最后一个参数。思考一下这是为什么?
但是呢,在 *
开头的参数后面,还是可以接参数的,将某个关键字参数放在 *
开头的函数或者一个单独的 *
后面,这个参数就会变成 keyword-only 参数,只能用关键字的形式使用。
例如:
def person(name, *, age):
pass
person("张三", 12) #错误:TypeError: person() takes 1 positional argument but 2 were given
person("张三", age=12) #True
这时候用第一种方式调用函数就会报错了,必须用关键字 age=12
的形式。
使用 keyword-only 参数可以提高代码可读性。
闭包(closure)
有时对于一些只有一个方法的类(除 __init__()
外),我们希望用一个函数来替代它以简化代码。
from urllib.request import urlopen
class UrlTemplate:
def __init__(self, template):
self.template = template
def open(self, **kwargs):
return urlopen(self.template.format_map(kwargs))
# 使用示例
yahoo = UrlTemplate('http://finance.yahoo.com/d/quotes.csv?s={names}&f={fields}')
for line in yahoo.open(names='IBM,AAPL,FB', fields='sllclv'):
print(line.decode('utf-8'))
这里的 UrlTemplate
类可以使用函数来替代:
def urltemplate(template):
def opener(**kwargs):
return urlopen(template.format_map(kwargs))
return opener
# 使用示例
yahoo = urltemplate('http://finance.yahoo.com/d/quotes.csv?s={names}&f={fields}')
for line in yahoo(names='IBM,AAPL,FB', fields='sllclv'):
print(line.decode('utf-8'))
闭包是一种嵌套形式的函数,外部函数直接把内部函数当作返回值返回。闭包顾名思义,就像一个包裹,它可以记住定义闭包时的环境。像示例中,opener()
函数可以记住 urltemplate()
参数 template
的值,并在之后的调用中使用。
装饰器
有时候我们想扩展函数的功能,但是又不想修改函数的代码,这时候就可以使用到装饰器。装饰器本质上是一个函数(或类),它接受一个函数做为输入并返回一个新的函数做为输出。
例如我们现在需要给我们的函数添加一个日志功能,打印出函数执行时间:
import time
from functools import wraps
def log(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("calling:{}".format(func.__name__))
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(func.__name__, end-start)
return result
return wrapper
# 使用示例
@log
def f(x):
while x < 1000000:
x += 1
调用结果:
>>> f(1)
calling:f
f 0.11393427848815918
>>>
如果不使用装饰器,我们要在 f()
函数中编写代码,不仅原本的函数变得面目全非,如果我们想在其它函数上也增加记录功能,又得复制代码过去,不如使用装饰器方便。
上面使用装饰器的部分,实质上相当于:
def f(x):
...
f = log(f)
装饰器函数接受 被装饰的函数 做为参数,并将原函数名这个变量重新赋值为返回后的新函数。
扫码关注公众号: