装饰器
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象
它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的代码并继续重用
1、函数调用
#定义一个函数
def hi(mes="我要玩亚索"):
return "中路是我的,"+mes
print(hi())
# 拷贝函数
greet = hi
print(greet())
try:
# 删除原函数
del hi
print(hi())
except Exception as e:
print("函数已被删除")
finally:
print(greet())
2、函数中调用函数
def hi(name="锐雯"):
print("now you are inside the hi() function")
def greet():
return "now you are inside the greet() function"
def welcone():
return "now you are inside the welcome() function"
print(greet())
print(welcone())
print("now you are back in the hi() function")
hi()
try:
# 在主函数中调用hi函数中的greet函数
greet()
except Exception as e:
print("函数不存在")
3、函数调用返回函数调用
def hi(name="锐雯"):
def greet():
return "now you are inside the greet() function"
def welcome():
return "now you are inside the welcome() function"
if name=="锐雯":
return greet
else:
return welcome
a = hi()
print(a)
print(a())
a = hi("亚索")
print(a())
4、函数传参–传入函数参数
def hi():
return "It's a nice day"
def doSomethingToSee(func):
print(func())
print("I should do something")
doSomethingToSee(hi)
5、函数传入函数参数返回函数调用
def a_new_decorator(a_func):
def wrapTheFunction():
print("在函数调用前我正在做一些无聊的事情")
a_func()
print("终于结束函数调用了,我都快累死了")
return wrapTheFunction
def a_function_requiring_decoration():
print("我是一个需要装饰的函数")
a_function_requiring_decoration()
a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
a_function_requiring_decoration()
上面的函数a_new_decorator就是一个装饰器
现在python允许装饰器调用使用@语法糖
下面使用@语法糖重构上述装饰器:
import functools
def a_new_decorator(a_func):
def wrapTheFunction():
print("在函数调用前我正在做一些无聊的事情")
a_func()
print("终于结束函数调用了,我都快累死了")
return wrapTheFunction
@a_new_decorator
def a_function_requiring_decoration():
print("我是一个需要装饰的函数")
print(a_function_requiring_decoration.__name__)
a_function_requiring_decoration()
上述语法糖"@a_new_decorator"等价于 未重构之前的“a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)“
注意:
我们发现“a_function_requiring_decoration._name_”返回的是wrapTheFunction,也就是说原函数在经历装饰器后函数的名字和函数的注释、文档都被修改成了装饰器闭包内的函数
为了防止这种情况的出现
我们导入一个模块
import functools/from functools import wraps
import functools
def a_new_decorator(a_func):
@functools.wraps(a_func)
def wrapTheFunction():
print("在函数调用前我正在做一些无聊的事情")
a_func()
print("终于结束函数调用了,我都快累死了")
return wrapTheFunction
@a_new_decorator
def a_function_requiring_decoration():
print("我是一个需要装饰的函数")
print(a_function_requiring_decoration.__name__)
a_function_requiring_decoration()
就可以解决这个问题了
也可以通过以下两个方法:
- 使用decorator.py文件
decorator.py 是一个非常简单的装饰器加强包。你可以很直观的先定义包装函数wrapper(),再使用decorate(func, wrapper)方法就可以完成一个装饰器。
decorator.py实现的装饰器能完整保留原函数的name,doc和args
唯一有问题的就是inspect.getsource(func)返回的还是装饰器的源代码需要改成inspect.getsource(func._wrapped_)
import decorator
import decorator
import datetime
# 装饰器的优化
def wrapper(func, *args, **kwargs):
print("我正在装扮自己")
return func(*args, **kwargs)
def logging(func):
# 用wrapper装饰func
return decorator.decorate(func, wrapper)
# @logging
def hi():
print("my name is Q")
a= logging(hi)
a()
- 使用 wrapt文件
import wrapt
wrapt是一个功能非常完善的包,用于实现各种装饰器
使用wrapt实现的装饰器不需要担心之前inspect中遇到的所有问题,因为它都帮你处理了,甚至inspect.getsource(func)也准确无误
import wrapt
@wrapt.decorator
def logging(wrapped, instance, *args, **kwargs):
print("我是个装饰器的外套")
return wrapped(*args, **kwargs)
@logging
def sayHi(*args, **kwargs):
print("Hi")
sayHi()
带参数的装饰器
1.基于函数实现的装饰器
在装饰器外再套一层装饰器,专门用于向装饰器内部传递参数
import functools
def add_2_somenumber(num, add_num=1):
print("---1---")
def know_whatToDo(func):
print("---2---")
@functools.wraps(func)
def wirte_function_name(*arg, **kwargs):
print("---3---")
print(func.__name__, " was called at here")
print("as a result, the answer is %d" % (num + add_num))
return func(*arg, **kwargs)
return wirte_function_name
return know_whatToDo
@add_2_somenumber(2,3)
def print_result():
print("开始活动了")
print("---4---")
print_result()
2.基于类实现的装饰器
装饰器函数其实是这样一个接口约束
它必须接受一个callable对象作为参数,然后返回一个callable对象
在Python中一般callable对象都是函数
但也有例外,只要某个对象重载了__call__()方法,那么这个对象就是callable的
在class对象中:
_init_:用来传递参数
_call_:用来构建装饰器
# 装饰类
import functools
class logit(object):
def __int__(self, logfile="out.log"):
self.login_file = logfile
def __call__(self, func):
@functools.wraps(func)
def wrapped_function(*arg, **kwarg):
login_string = func.__name__+" was called "
print(login_string)
return func(*arg, **kwarg)
return wrapped_function
@logit(logfile="out.log")
def myfunc_test():
print("这是类装饰器的产生")
myfunc_test()
1)不能修改原函数
2)不能修改调用方式
2、装饰器通过嵌套函数和闭包实现
3、装饰器执行顺序:洋葱法则
4、装饰器通过语法糖“@”修饰
5、谨记装饰器返回的是持有被装饰函数引用的闭包函数的引用这条原则
常用的装饰器
属性有三个装饰器:setter, getter, deleter ,都是在property()的基础上做了一些封装
因为setter和deleter是property()的第二和第三个参数,不能直接套用@语法
class Classmate:
def __init__(self):
self.age = 15
@property
def set_name(self, value):
self.name = value
student = Classmate()
student.name="Q"
print(student.age)
print(student.name)
有了@property装饰器的了解,这两个装饰器的原理是差不多的
@staticmethod返回的是一个staticmethod类对象
@classmethod返回的是一个classmethod类对象
他们都是调用的是各自的__init__()构造函数
迭代器
from collections.abc import Iterable
from collections.abc import Iterator
content = list()
content = [x*x for x in range(10)]
print(type(content))
可以发现 常见的列表、字典、集合、元祖、字符串都属于迭代对象
print(isinstance([], Iterable))
print(isinstance((), Iterable))
print(isinstance({}, Iterable))
print(isinstance("", Iterable))
迭代对象都可以通过for遍历
for i in content:
print(i, end=" ")
print()
类能不能是迭代对象呢?
class ClassMate(object):
def __init__(self):
self.names = []
def add_name(self, name):
self.names.append(name)
student = ClassMate()
student.add_name("老王")
student.add_name("小张")
student.add_name("小李")
print(isinstance(student, Iterable))
发现普通的类不是可迭代对象
只用当当类对象中出现__iter__方法时,该类就变成了可迭代对象
class ClassMate(object):
def __init__(self):
self.names = []
def add_name(self, name):
self.names.append(name)
def __iter__(self):
pass
student = ClassMate()
student.add_name("老王")
student.add_name("小张")
student.add_name("小李")
# 当类对象中出现__iter__方法时,该类就变成了可迭代对象
print(isinstance(student, Iterable))
# 我们使用for循环遍历,发现还是不能够迭代类对象
# 这时候我们就可以使用类中的一个方法__next__,使其迭代对象
try:
for i in student:
print(i)
except Exception as e:
print("不能够进行迭代")
1. 通过一个可迭代的类来调用原本自身类的迭代
class ClassMate(object):
def __init__(self):
self.names = []
self.index = 0
def add_name(self, name):
self.names.append(name)
def __iter__(self):
# 返回一个可以迭代的对象
return ClassRoom(self)
def __next__(self):
if self.index+1 <= len(self.names):
result = self.names[self.index]
self.index += 1
return result
else:
# 如果没有这句话,对象将会一直返回None
# 这里报错StopIteration,并停止迭代
raise StopIteration
class ClassRoom(object):
def __init__(self, obj):
self.index = 0
self.obj = obj
def __iter__(self):
pass
def __next__(self):
if self.index + 1 <= len(self.obj.names):
result = self.obj.names[self.index]
self.index += 1
return result
else:
# 如果没有这句话,对象将会一直返回None
# 这里报错StopIteration,并停止迭代
raise StopIteration
student = ClassMate()
student.add_name("老王")
student.add_name("小张")
student.add_name("小李")
for i in student:
print(i)
2. 自己迭代
class ClassMate(object):
def __init__(self):
self.names = []
self.index = 0
def add_name(self, name):
self.names.append(name)
def __iter__(self):
# 返回一个可以迭代的对象
return self
def __next__(self):
if self.index+1 <= len(self.names):
result = self.names[self.index]
self.index += 1
return result
else:
# 如果没有这句话,对象将会一直返回None
# 这里报错StopIteration,并停止迭代
raise StopIteration
student = ClassMate()
student.add_name("老王")
student.add_name("小张")
student.add_name("小李")
for i in student:
print(i)
生成器
生成器是一种特殊的迭代器
普通的生成器
只需要把迭代器生成时的中括号改成小括号
def get_test1(num):
print(num)
i = 0
print(i, end=" ")
while i < num:
print(i, end=" ")
ret = yield i
i += 1
n = get_test1(10)
for i in range(10):
next(n)
def get_test2(num):
print(num)
i = 0
print(i, end=" ")
while i < num:
print(i, end=" ")
ret = yield i
print(ret)
i += 1
n1 = get_test2(10)
next(n1)
n1.send("hahahaha")
next(n1)
当函数调用时出现了yield,它就变成了一个生成器
生成器可以通过_next_来获取数据
obj.send(msg)当程序进行时,使yield调用拥有返回值,其值为send传输过去的msg
注意:send方法调用时必须先调用next方法开始迭代生成
当子函数运行到yield时,程序会先将数值返回个函数调用,然后停止等待,一直到下一次获取生成器中数据时才启动