Python函数使用技巧
可接受任意数量参数的函数
在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)
装饰器函数接受被装饰的函数做为参数,并将原函数名这个变量重新赋值为返回后的新函数。
扫码关注公众号: