python装饰器详解
1. 作用域
在python中,作用域分为两种:全局作用域和局部作用域。
全局作用域是定义在文件级别的变量,函数名。而局部作用域,则是定义函数内部。
关于作用域,我们要理解两点:
- 在全局不能访问到局部定义的变量
- 在局部能够访问到全局定义的变量,但是不能修改全局定义的变量(当然有方法可以修改)
x = 1
def funx():
x = 10
print(x) # 打印出10
funx()
print(x) # 打印出1
# 如果局部没有定义变量x,那么函数内部会从内往外开始查找x,如果没有找到,就会报错
关于作用域的问题,只需要记住两点就行:
- 全局变量能够被文件任何地方引用,但修改只能在全局进行操作;
- 如果局部没有找到所需的变量,就会往外进行查找,没有找到就会报错
2. 高级函数
函数名其实就是指向一段内存空间的地址,既然是地址,那么可以利用这种特性得出:
2.1 函数名可以作为一个值
def search(cmd):
filename = cmd[-1]
pattern = cmd[1]
with open(filename, 'r', encoding="utf-8") as f:
for line in f:
if pattern in line:
print(line, end="")
else:
print("没有找到")
dic_func = {'delete': delete, 'add': add, 'modify': modify, 'search': search}
while True:
inp = input("请输入您要进行的操作:").strip()
if not inp:
continue
cmd_1 = inp.split()
cmd = cmd_1[0]
if cmd in dic_func:
dic_func[cmd](cmd_1)
else:
print("Error")
2.2 函数名可以作为返回值
def outer():
def inner():
pass
return inner
print(outer())
######输出结果为#######
<function outer.<locals>.inner at 0x000000D22D8AB8C8>
2.3 函数名可以作为一个参数
def index():
print("index func")
def outer(index):
func = index
func()
outer(index)
######输出结果#########
index func
3 闭包
闭包函数必须满足两个条件:
- 函数内部定义的函数
- 包含对外部作用域而非全局作用域的引用
def outer(y):
x = 1
def inner():
print("x= %s" %x)
print("y= %s" %y)
return inner
outer()
上面实例中,我们可以发现,闭包函数,它必须包含自己的函数以及一个外部变量才能真正称得上是一个闭包函数。如果没有一个外部变量与其绑定,那么這个函数不能算得上是闭包函数。上述例子中,参数 y 也是一个非全局的外部变量
4 装饰器
- 装饰器
- 外部函数传入被装饰函数名
- 内部函数返回装饰函数名
- 特点
- 不修改被装饰函数的调用方式
- 不修改被装饰函数的源代码
4.1 无参装饰器
import time, random
def outer(func): # 将index的地址传递给func
def inner():
start_time = time.time()
func() # fun = index 即func保存了外部index函数的地址
end_time = time.time()
print("运行时间为%s"%(end_time - start_time))
return inner # 返回inner的地址
def index():
time.sleep(random.randrange(1, 5))
print("welcome to index page")
index = outer(index) # 这里返回的是inner的地址,并重新赋值给index
index()
上述实例即可简写为
import time, random
def outer(func): # 将index的地址传递给func
def inner():
start_time = time.time()
func() # fun = index 即func保存了外部index函数的地址
end_time = time.time()
print("运行时间为%s"%(end_time - start_time))
return inner # 返回inner的地址
@outer
def index():
time.sleep(random.randrange(1, 5))
print("welcome to index page")
index()
4.2 有参装饰器
4.3 多个有参装饰器
def outer(func): # 将index的地址传递给func
def inner(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs) # fun = index 即func保存了外部index函数的地址
end_time = time.time()
print("运行时间为%s"%(end_time - start_time))
return inner # 返回inner的地址
@outer
def index(name):
time.sleep(random.randrange(1, 5))
print("welcome to index page")
index('name')
# 如果有返回值,则以下处理
def timmer(func):
def wrapper(*args,**kwargs):
start_time = time.time()
res=func(*args,**kwargs) #res来接收home函数的返回值
stop_time=time.time()
print('run time is %s' %(stop_time-start_time))
return res
return wrapper
@timmer
def home(name):
time.sleep(random.randrange(1,10))
print('HOME page %s' % name)
return 12
4.3 多个有参装饰器
多个装饰器装饰一个函数,其执行顺序是从下往上
import time
import random
def timmer(func):
def wrapper():
start_time = time.time()
func()
stop_time=time.time()
print('run time is %s' %(stop_time-start_time))
return wrapper
def auth(func):
def deco():
name=input('name: ')
password=input('password: ')
if name == 'egon' and password == '123':
print('login successful')
func() #wrapper()
else:
print('login err')
return deco
@auth # index = auth(timmer(index))
@timmer # index = timmer(index)
def index():
time.sleep(3)
print('welecome to index page')
index()
4.2 类装饰器
装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法
class Foo(object):
def __init__(self, func):
self._func = func
def __call__(self):
print('class decorator runing')
self._func()
print('class decorator ending')
@Foo # bar = Foo(bar)
def bar():
print('bar')
bar() # Foo(bar)()
# 结果
# class decorator runing
# bar
# class decorator ending
class Foo(object):
def __init__(self):
pass
def __call__(self, func):
def _call(*args, **kw):
print('class decorator runing')
return func(*args, **kw)
return _call
class Bar(object):
@Foo()
def bar(self, test, ids): # bar = Foo()(bar)
print('bar')
Bar().bar('aa', 'ids')