匿名函数
当我们在传入函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方便。如:list(map(lambda x:x * x,[1,2,3,4,5]))
实际上就相当于
def f(x):
return x * x
关键字lambda表示匿名函数,冒号前面的x表示函数参数。匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。同样,也可以把匿名函数作为返回值返回,比如:
def build(x, y):
return lambda: x * x + y * y
用匿名函数可以简化代码,一个筛选出奇数例子如下:
def is_odd(n):
return n % 2 == 1
L = list(filter(is_odd,range(1,20)))#用普通函数
L = list(filter(lambda x : x % 2 == 1,range(1,20)))#用匿名函数
装饰器
假设我们要增强函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。我们要定义一个能打印日志的decorator,可以定义如下:
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
再定义一个函数now()前面用@语法将decorator置于函数的定义处,相当于执行了语句:now = log(now)
@log
def now():
print('2019-1-4')
调用now()函数,不仅会运行now()函数本身,还会在运行now()函数前打印一行日志:
>>> now()
call now():
2019-1-4
这里需要注意经过decorator装饰之后的函数,它们的__name__
已经从原来的'now'
变成了'wrapper'
,所以,需要把原始函数的__name__
等属性复制到wrapper()
函数中,Python内置的functools.wraps
就是用来干这件事的,所以,一个完整的decorator的写法如下:
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
写了一个能打印出函数执行时间的例子:
import time, functools
def metric(fn):
@functools.wraps(fn)
def wrapper(*args, **kw):
start_time = time.time()
ret = fn(*args, **kw)
end_time = time.time()
print('%s executed in %s ms' % (fn.__name__, (end_time - start_time)))
return ret
return wrapper
@metric
def fast(x,y):
time.sleep(0.1)
return x + y
f = fast(11,22)
偏函数
Python的functools
模块提供了很多有用的功能,其中一个就是偏函数(Partial function)。它可以固定住原函数的部分参数,比如int()
函数可以把字符串转换为整数,当仅传入字符串时,int()
函数默认按十进制转换,int()
函数还提供额外的base参数,默认值为10。如果传入base参数,就可以做N进制的转换:
>>> int('12345', base=8)
5349
>>> int('12345', 16)
74565
假设要转换大量的二进制字符串,每次都传入int(x, base=2)
非常麻烦,可以定义一个int2()的函数,默认把base=2传进去,functools.partial
就是帮助我们创建一个偏函数的,不需要我们自己定义int2()
,如:
import functools
int2 = functools.partial(int, base=2)
所以,简单总结偏函数
的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。上面的新的int2
函数,仅仅是把base参数重新设定默认值为2,但也可以在函数调用时传入其他值。
python的程序入口
对于很多编程语言来说,程序都必须要有一个入口,比如C,C++,C,C++都需要有一个main函数作为程序的入口,也就是程序的运行会从main函数开始。而Python则不同,它属于脚本语言,不像编译型语言那样先将程序编译成二进制再运行,而是动态的逐行解释运行。也就是从脚本第一行开始运行,没有统一的入口。
假设我们有一个const.py文件:
PI = 3.14
def main():
print("PI:", PI)
main()
# 运行结果:PI: 3.14
然后写一个计算面积的程序,用到const.py里的PI:
from const import PI
def calc_round_area(radius):
return PI * (radius ** 2)
def main():
print("round area: ", calc_round_area(2))
main()
'''
运行结果:
PI: 3.14
round area: 12.56
'''
可以看到const.py中的main函数也被运行了,实际上我们不希望它被运行,这时就需要if __name__ == '__main__'
了:
PI = 3.14
def main():
print("PI:", PI)
if __name__ == "__main__":
main()
运行const.py,输出如下:PI: 3.14
,运行计算面积的程序,输出如下:round area: 12.56
,所以if __name__ == '__main__'
相当于Python模拟的程序入口,这是一种编程的习惯.
__name__
是内置变量,表示当前模块的名字,如果一个.py文件(模块)被直接运行时,则其没有包结构,其__name__
值为__main__
,即模块名为__main__
。
所以,if __name__ == '__main__'
的意思是:当.py文件被直接运行时,if __name__ == '__main__'
之下的代码块将被运行;当.py文件以模块形式被导入时,if __name__ == '__main__'
之下的代码块不被运行。