datetime:2019/5/16 13:23
author:nzb
函数的使用方式
将函数视为“一等公民”
- 函数可以赋值给变量
- 函数可以作为函数的参数
- 函数可以作为函数的返回值
高阶函数的用法(
filter
、map
以及它们的替代品)
items1 = list(map(lambda x: x ** 2, filter(lambda x: x % 2, range(1, 10))))
items2 = [x ** 2 for x in range(1, 10) if x % 2]
位置参数、可变参数、关键字参数、命名关键字参数
参数的元信息(代码可读性问题)
匿名函数和内联函数的用法(
lambda
函数)闭包和作用域问题
闭包
函数内的属性,都是有生命周期,都是在函数执行期间
内部函数对外部函数作用域里变量的引用
闭包内的闭包函数私有化了变量,完成了数据的封装,类似面向对象
作用域
Python搜索变量的LEGB顺序(Local --> Embedded --> Global --> Built-in)
global
和nonlocal
关键字的作用global
:声明或定义全局变量(要么直接使用现有的全局作用域的变量,要么定义一个变量放到全局作用域)。nonlocal
:声明使用嵌套作用域的变量(嵌套作用域必须存在该变量,否则报错)。
装饰器函数(使用装饰器和取消装饰器)语法糖 @
最简单的例子:
def func1(func): # 外部闭包函数的参数是被装饰的函数对象
def func2():
print('aaabbb')
return func() # 返回了外部函数接收的被装饰函数的调用
return func2
# return func # 返回了函数对象
# return func() # 返回的是一个函数调用
# func1(myfunc)() # 接收别装饰的函数作为参数,而且还要继续调用一次
# func2() -> print('aaabbb') -> return myfunc()
@func1
def myfunc():
print('你好')
# 不影响原有函数的功能,还能添加新的功能
myfunc() # func1(myfunc)()
装饰器函数带参数(与下面一样)多一层包装来接收装饰器的参数
def arg_func(sex):
def func1(b_func):
def func2():
if sex == 'man':
print('你是男士')
if sex == 'woman':
print('你是女士')
return b_func()
return func2
return func1
@arg_func(sex='man')
def man():
print('好好上班')
@arg_func(sex='woman')
def woman():
print('好好上班')
man()
woman()
例子:输出函数执行时间的装饰器。
def record_time(func):
"""自定义装饰函数的装饰器"""
@wraps(func)
def wrapper(*args, **kwargs): # 被装饰的函数带参数(最常见)
start = time()
result = func(*args, **kwargs) # 被装饰的函数带参数(最常见)
print(f'{func.__name__}: {time() - start}秒')
return result
return wrapper
如果装饰器不希望跟print
函数耦合,可以编写带参数的装饰器。
from functools import wraps
from time import time
def record(output):
"""自定义带参数的装饰器"""
def decorate(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time()
result = func(*args, **kwargs)
output(func.__name__, time() - start)
return result
return wrapper
return decorate
from functools import wraps
from time import time
class Record():
"""自定义装饰器类(通过__call__魔术方法使得对象可以当成函数调用)"""
def __init__(self, output):
self.output = output
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time()
result = func(*args, **kwargs)
self.output(func.__name__, time() - start)
return result
return wrapper
说明:由于对带装饰功能的函数添加了@wraps装饰器,可以通过
func.__wrapped__
方式获得被装饰之前的函数或类来取消装饰器的作用。
例子:用装饰器来实现单例模式。
from functools import wraps
def singleton(cls):
"""装饰类的装饰器"""
instances = {}
@wraps(cls)
def wrapper(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return wrapper
@singleton
class President():
"""总统(单例类)"""
pass
说明:上面的代码中用到了闭包(closure),不知道你是否已经意识到了。还有一个小问题就是,上面的代码并没有实现线程安全的单例,如果要实现线程安全的单例应该怎么做呢?
from functools import wraps
def singleton(cls):
"""线程安全的单例装饰器"""
instances = {}
locker = Lock()
@wraps(cls)
def wrapper(*args, **kwargs):
if cls not in instances:
with locker:
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return wrapper
装饰器模板
装饰器的作用:装饰器即可以装饰函数也可以装饰类。
装饰器的原理:函数也是对象使用装饰器:假设decorator是定义好的装饰器。
方法一:不用@符号
# 装饰器不传入参数时 f = decorator(函数名) # 装饰器传入参数时 f = (decorator(参数))(函数名) 或 decorator(参数)(函数名)
方法二:使用@符号
# 已定义的装饰器 @decorator def f(): pass # 执行被装饰过的函数 f()