1.魔法方法__call__()
让类的实例化对象可以像函数一样被调用
class Fib(object):
def __init__(self):
pass
def __call__(self, num):
a,b = 0,1
self.lst = []
for i in range(num):
self.lst.append(a)
a,b = b,a+b
return self.lst
def __str__(self):
return str(self.lst)
f = Fib()
f(10)
print(f)
2.闭包
在函数内部再定义一个函数,并且内部函数用到了外部函数作用域里的变量(enclosing),那么将这个内部函数以及用到的外部函数内的变量一起称为闭包(Closure)
In [1]: def line(a, b):
...: def get_y_axis(x):
...: return a * x + b # 内部函数使用了外部函数的变量a和b
...: return get_y_axis # 返回值是闭包函数名,注意不是函数调用没有小括号
...:
...:
In [2]: L1 = line(1, 1) # 创建一条直线: y=x+1
In [3]: L1
Out[3]: .get_y_axis(x)>
In [4]: L1(3) # 获取第一条直线中,横坐标是3时,纵坐标的值
Out[4]: 4
In [5]: L2 = line(2, 3) # 创建一条直线: y=2x+3
In [6]: L2
Out[6]: .get_y_axis(x)>
In [7]: L2(3) # 获取第二条直线中,横坐标是3时,纵坐标的值
Out[7]: 9
In [8]: L3 = line(2, 3) # 再创建一条直线: y=2x+3
In [9]: L3
Out[9]: .get_y_axis(x)>
In [10]: L2 == L3 # 每次调用line()返回的都是不同的函数,即使传入相同的参数
Out[10]: False
In [11]: L2 is L3
Out[11]: False
注意: 闭包中不要引用外部函数中任何循环变量或后续会发生变化的变量
# 1. 错误的用法
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
# f1()、f2()和f3()的输出结果都是9,原因是调用这三个函数时,闭包中引用的外部函数中变量i的值已经变成3
# 2. 正确的用法
def count():
def f(j):
return lambda: j * j
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入闭包lambda: j * j
return fs
f1, f2, f3 = count()
print(f1()) # 输出1
print(f2()) # 输出4
print(f3()) # 输出9
3.装饰器
装饰器(decorator)接受一个callable对象(可以是函数或者实现了call方法的类)作为参数,并返回一个callable对象
3.1被装饰的函数无参数
没有使用装饰器:
def f1():
print('function f1...')
def f2():
print('function f1...')
f1() # 输出function f1...
f2() # 输出function f2...
使用装饰器:
def login_required(func):
def inner(): # inner是一个闭包,它使用了外部函数的变量func,即传入的原函数引用f1、f2...
if func.__name__ == 'f1': # 这里是权限验证的逻辑判断,此处简化为只能调用f1
print(func.__name__, ' 权限验证成功')
func() # 执行原函数,相当于f1()或f2()...
else:
print(func.__name__, ' 权限验证失败')
return inner
def f1():
print('function f1...')
def f2():
print('function f1...')
f1 = login_required(f1) # 将f1引用传入装饰器,此时func指向了原f1函数体。返回inner引用,并赋值给f1,即现在是func指向原函数体,而f1重新指向了返回的inner闭包
f1() # 执行函数,即执行inner(),这个闭包中使用的func变量指向原f1函数体
f2 = login_required(f2) # 将f2引用传入装饰器,此时func指向了原f2函数体。返回inner引用,并赋值给f2,即现在是func指向原函数体,而f1重新指向了返回的inner闭包
f2() # 执行函数,即执行inner(),func变量指向原f2,所以它不会通过权限验证,即不会执行func()
# 输出结果:
f1 权限验证成功
function f1...
f2 权限验证失败
可以用@Python语法糖简写为:
# 1. 定义时
@login_required
def f1():
print('function f1...')
# 2. 调用时
f1()
3.2被装饰的函数有参数
def print_name(func):
def inner(*args, **kwargs):
print(func.__name__) # 是对原有函数的加强
# result = func(*args,**kwargs) #保持原有函数功能不变
# return result
func(*args, **kwargs)
return inner
@print_name
def admin():
print('function admin...')
@print_name
def f2():
print('function f2...')
@print_name
def f1(a, b, c='dd'):
print('function f1, args: ', a, b, c)
result = f1('hello', 3)
print(result)
#输出
f1
function f1, args: hello 3 dd
3.3被装饰的函数有返回值
def print_name(func):
def inner(*args, **kwargs):
print(func.__name__) # 是对原有函数的加强
# result = func(*args,**kwargs) #保持原有函数功能不变
# return result
return func(*args, **kwargs) # 被装饰函数的返回
return inner
@print_name
def admin():
print('function admin...')
@print_name
def f2():
print('function f2...')
@print_name
def f1(a, b, c='dd'):
print('function f1, args: ', a, b, c)
return 'hello, world'
result = f1('hello', 3)
print(result)
#输出
f1
function f1, args: hello 3 dd
hello, world
3.4装饰器带参数
import logging
# logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
from functools import wraps
def mylogging(level):
if level == 'warning':
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.WARNING)
elif level == 'info':
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
else:
pass
def log(func):
@wraps(func)
def inner(x): # @wraps是为了输出原函数的签名
logging.debug('This is debug message'+func.__name__)
logging.info('This is info message'+func.__name__)
logging.warning('This is warning message'+func.__name__)
logging.error('This is error message'+func.__name__)
logging.critical('This is critical message'+func.__name__)
func(x)
return inner
return log
@mylogging('warning')
def f1(x):
print('f1 is called args:' + str(x))
f1(123)
print(f1.__name__)
# 输出
2019-12-01 16:18:18,981 - root - WARNING - This is warning messagef1
2019-12-01 16:18:18,982 - root - ERROR - This is error messagef1
2019-12-01 16:18:18,982 - root - CRITICAL - This is critical messagef1
f1 is called args:123
f1
3.5多个装饰器装饰同一个函数
def make_b(func):
print('这个是加粗装饰器')
def inner():
result = func()
return '' + result + ''
return inner
def make_i(func):
print('这个是变斜装饰器')
def inner():
result = func()
return '' + result + ''
return inner
@make_i
@make_b
def foo():
return 'hello'
res = foo()
print(res)
# 输出
这个是加粗装饰器
这个是变斜装饰器
hello
3.6基于类实现的装饰器
class logging(object):
def __init__(self, level):
self._level = level
def __call__(self, func): # 接受函数
def inner(*args, **kwargs):
print('[日志级别 {}]: 被装饰的函数名是 {}'.format(self._level, func.__name__))
return func(*args, **kwargs)
return inner
@logging('DEBUG')
def f1(a, b, c):
"""This is f1 function"""
print('function f1, args: a={}, b={}, c={}'.format(a, b, c))
return 'hello, world'
res = f1(10, 20, 30)
print(res)
# 输出
[日志级别 DEBUG]: 被装饰的函数名是 f1
function f1, args: a=10, b=20, c=30
hello, world