以下内容是整理极客时间Python核心技术与实战课程的笔记。
Python 函数嵌套
函数嵌套,也就是指函数里面又有函数。
def f1():
print('hello')
def f2():
print('world')
f2()
f1()
# 输出
hello
world
函数嵌套的优势?
第一点:函数的嵌套能够保证内部函数的隐私。内部函数只能被外部函数所调用和访问,不会暴露在全局作用域,因此,如果你的函数内部有一些隐私数据(比如数据库的用户、密码等),不想暴露在外,那你就可以使用函数的的嵌套,将其封装在内部函数中,只通过外部函数来访问。我们只能通过调用外部函数来访问它,这样一来,程序的安全性便有了很大的提高。
def connect_DB():
def get_DB_configuration():
...
return host, username, password
conn = connector.connect(get_DB_configuration())
return conn
第二点:合理的使用函数嵌套,能够提高程序的运行效率。如果不使用函数嵌套,那么每调用一次递归便会检查一次,这是没有必要的,也会降低程序的运行效率。
def factorial(input):
# validation check
if not isinstance(input, int):
raise Exception('input must be an integer.')
if input < 0:
raise Exception('input must be greater or equal to 0' )
...
def inner_factorial(input):
if input <= 1:
return 1
return input * inner_factorial(input-1)
return inner_factorial(input)
print(factorial(5))
所以合理的使用嵌套函数,能保证数据的隐私性,提高程序运行效率。
闭包
闭包和嵌套函数类似,不同的是,这里外部函数返回的是一个函数,而不是一个具体的值。返回的函数通常赋于一个变量,这个变量可以在后面被继续执行调用。
外部函数 nth_power() 返回值,是函数 exponent_of()。
def nth_power(exponent):
"""返回值是 exponent_of 函数"""
def exponent_of(base):
return base ** exponent
return exponent_of # 返回值是 exponent_of 函数
# 计算一个数的平方
square = nth_power(2)
# 计算一个数的立方
cube = nth_power(3)
print(square(2)) # 计算 2 的平方
# 4 # 2^2
print(cube(2)) # 计算 2 的立方
# 8 # 2^3
为什么要闭包呢?
上面的程序,也可以写成下面的形式。
def nth_power_rewrite(base, exponent):
return base ** exponent
闭包是让程序变得更简洁易读。比如你需要计算很多个数的平方,闭包现在就很简洁易懂了。
# 不适用闭包
res1 = nth_power_rewrite(base1, 2)
res2 = nth_power_rewrite(base2, 2)
res3 = nth_power_rewrite(base3, 2)
...
# 使用闭包
square = nth_power(2)
res1 = square(base1)
res2 = square(base2)
res3 = square(base3)
所以合理地使用闭包,则可以简化程序的复杂度,提高可读性。
匿名函数
匿名函数搭配map()、filter() 和 reduce()使用。
map
map(function, iterable) 函数,对 iterable 中的每个元素,都运用 function 这个函数,最后返回一个新的可遍历的集合。
对于下面这个例子,要对列表中的每个元素乘以 2,返回一个集合。
l = [1, 2, 3, 4, 5]
new_list = map(lambda x: x * 2, l) # [2, 4, 6, 8, 10]
map() 函数直接由 C 语言写的,运行时不需要通过 Python 解释器间接调用,并且内部做了诸多优化,相比于for 循环和 list,运行速度更快。
filter
filter(function, iterable) 函数,它和 map 函数类似,function 同样表示一个函数对象。filter() 函数表示对 iterable 中的每个元素,都使用 function 判断,并返回 True 或者 False,最后将返回 True 的元素组成一个新的可遍历的集合。
返回一个列表中的偶数。
l = [1, 2, 3, 4, 5]
# [2, 4]
new_list = filter(lambda x: x % 2 == 0, l)
reduce
reduce(function, iterable) 函数,它通常用来对一个集合做一些累积操作。它有两个参数,表示对 iterable 中的每个元素以及上一次调用后的结果,运用 function 进行计算,所以最后返回的是一个单独的数值。
计算某个列表元素的乘积,就可以用 reduce() 函数来表示。
l = [1, 2, 3, 4, 5]
# 1*2*3*4*5 = 120
product = reduce(lambda x, y: x * y, l)
对于相加、累积这种简单操作,优先考虑 map、filter、reduce 。
装饰器
所谓的装饰器,其实就是通过装饰器函数,来修改原函数的一些功能,使得原函数不需要修改。
在 Python 中,函数是一等公民,同时函数也是对象。
###一个例子
# -*- coding: utf-8 -*-
def my_decorator(func):
def wrapper():
print('wrapper of decorator')
func()
return wrapper
@my_decorator
def greet():
print('hello world')
greet()
# 输出
#wrapper of decorator
#hello world
@my_decorator就相当于把函数greet作为参数和传给my_decorator函数,也就是greet=my_decorator(greet),它把真正需要执行的函数== greet==() 包裹在自己内部,并且改变了它的行为,但是原函数 greet() 不变。
若greet函数有参数,通常情况下,我们会把*args和**kwargs,作为装饰器内部函数 wrapper() 的参数。*args和**kwargs,表示接受任意数量和类型的参数,因此装饰器就可以写成下面的形式:
def my_decorator(func):
def wrapper(*args, **kwargs):
print('wrapper of decorator')
func(*args, **kwargs)
return wrapper
用法如下:
# -*- coding: utf-8 -*-
def my_decorator(func):
def wrapper(*args, **kwargs):
print('wrapper of decorator')
func(*args, **kwargs)
return wrapper
@my_decorator
def greet(str1,str2):
print('hello world')
print(str1)
print(str2)
greet("python","function")
#wrapper of decorator
#hello world
#python
#function
装饰器的用处
身份认证、日志记录、输入合理性检查、缓存等。
身份认证,比如用户对某篇文章的评论。都会先检查用户是否处于登录状态,如果是登录状态,则允许这项操作;如果没有登录,则不允许。
例子
import functools
def authenticate(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
request = args[0]
if check_user_logged_in(request): # 如果用户处于登录状态
return func(*args, **kwargs) # 执行函数 post_comment()
else:
raise Exception('Authentication failed')
return wrapper
@authenticate
def post_comment(request, ...)
...
日志记录:统计函数的耗时,装饰器 log_execution_time 记录某个函数的运行时间,并返回其执行结果。如果你想计算任何函数的执行时间,在这个函数上方加上@log_execution_time即可。
import time
import functools
def log_execution_time(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
res = func(*args, **kwargs)
end = time.perf_counter()
print('{} took {} ms'.format(func.__name__, (end - start) * 1000))
return res
return wrapper
@log_execution_time
def calculate_similarity(items):
...
输入合理性检查:比如用装饰器对其输入(往往是很长的 json 文件)进行合理性检查。从而避免输入不正确对机器造成的巨大开销。
import functools
def validation_check(input):
@functools.wraps(func)
def wrapper(*args, **kwargs):
... # 检查输入是否合法
@validation_check
def neural_network_training(param1, param2, ...):
...
做机器学习的知道,在模型训练的时候,数据量大,若处理半天,读取数据不对是一个很耗时的过程,所以这一步在这点上非常有必要。
缓存:python内部的lru算法就是使用装饰器@lru_cache。