python是一种面向对象的编程语言,在python中一切皆对象,这样就使得变量所拥有的属性,函数也同样拥有。这样我们就可以理解在函数内创建一个函数的行为是完全合法的。这种函数被叫做内嵌函数,这种函数只可以在外部函数的作用域内被正常调用,在外部函数的作用域之外调用会报错。
而如果内部函数里引用了外部函数里定义的对象(甚至是外层之外,但不是全局变量),那么此时内部函数就被称为闭包函数。闭包函数所引用的外部定义的变量被叫做自由变量。闭包从语法上看非常简单,但是却有强大的作用。闭包可以将其自己的代码和作用域以及外部函数的作用结合在一起。下面给出一个简单的闭包的例子:
def count():
a = 1
b = 1
def sum():
c = 1
return a + c # a - 自由变量
return sum
条件:函数内部定义的函数;引用了外部变量但非全局变量。
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象(函数的指针)。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。
实质: 是一个函数
参数:是你要装饰的函数名(并非函数调用)
返回:是装饰完的函数名(也非函数调用)
作用:为已经存在的对象添加额外的功能
特点:不需要对对象做任何的代码上的变动
给函数添加计时功能:
import time
def decorator(func):
def wrapper(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs)
end_time = time.time()
print(end_time - start_time)
return wrapper
@decorator
def func():
time.sleep(0.8)
func() # 函数调用# 输出:0.8003056049346924
@decorator这个语法相当于 执行
func = decorator(func)
,为func函数装饰并返回。装饰器函数- decorator
,该函数的传入参数是func
(被装饰函数),返回参数是内层函数。这里的内层函数-wrapper
,其实就相当于闭包函数,它起到装饰给定函数的作用,wrapper参数为*args, **kwargs
。*args表示的参数以列表的形式传入;**kwargs表示的参数以字典的形式传入。凡是以key=value形式的参数均存在kwargs中,剩下的所有参数都以列表的形式存于args中。使用*args, **kwargs
可以保证装饰器可以应用于任何目标函数,否则需要保证内层函数wrapper和被装饰函数func的传入参数和返回值类型必须保持一致。
def logging(level):
def wrapper(func):
def inner_wrapper(*args, **kwargs):
print("[{level}]: | {func}()".format(level=level, func=func.__name__))
return func(*args, **kwargs)
return inner_wrapper
return wrapper
@logging(level=‘INFO‘)
def say(something):
print("say {}!".format(something))
say(‘hello‘)
# 输出结果为:
# [INFO]: | say()
# say hello!
上述代码使用了format方式,此外可以使用%方式
import time
def decorator(func):
def wrapper(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs)
end_time = time.time()
print(end_time - start_time)
return wrapper
class Method(object):
@decorator
def func(self):
time.sleep(0.8)
Method().func() # 函数调用
将一个类当做装饰器进行使用
class Decorator(object):
def __init__(self, f):
self.f = f
def __call__(self):
print("decorator start")
self.f()
print("decorator end")
@Decorator
def func():
print("func")
func()
call()是一个特殊方法,它可将一个类实例变成一个可调用对象:
p = Decorator(func) # p是类Decorator的一个实例
p() # 实现了__call__()方法后,p可以被调用
要使用类装饰器必须实现类中的__call__()方法,就相当于将实例变成了一个方法。
多个装饰器的执行顺序:是从近到远依次执行(从最下方的开始)。
def decorator(func):
def inner_function():
pass
return inner_function
@decorator
def func():
pass
print(func.__name__)
# 输出: inner_function
从结果看被装饰函数自身的信息丢失了,为了避免这种情况,需要使用借助functools.wraps()
函数
from functools import wraps
def decorator(func):
@wraps(func)
def inner_function():
pass
return inner_function
@decorator
def func():
pass
print(func.__name__)
#输出: func
内置的装饰器和普通的装饰器原理是一样的,只不过返回的不是函数,而是类对象
@property
装饰器:将方法变成属性调用的,可快速实现set和get方法class Student:
# 构造函数,私有化变量
def __init__(self,name,score):
self.__name=name
self.__score=score
@property
def score(self):
return self.__score
@score.setter
def score(self,score):
if 0<score<101:
self.__score=score
else:
print(‘请输入正确的分数!‘)
st1=Student("小明",98)
# 调用get方法
print(st1.score)
# 调用set方法
st1.score=68
print(st1.score)
@staticmethod
和@classmethod
装饰器作用:使用上述两个装饰器后,不需要实例化对象再调用方法,可以直接使用类名.方法名调用
差异:@staticmethod
不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。@classmethod
也不需要self参数,但第一个参数需要是表示自身类的cls参数。
先定义包装函数wrapper()
,再使用decorate(func, wrapper)
方法就可以完成一个装饰器。
from decorator import decorate
def wrapper(func, *args, **kwargs):
"""print log before a function."""
print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)
return func(*args, **kwargs)
def logging(func):
return decorate(func, wrapper) # 用wrapper装饰func
或者使用自带的@decorator
装饰器来完成
from decorator import decorator
@decorator
def logging(func, *args, **kwargs):
print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)
return func(*args, **kwargs)
decorator.py
实现的装饰器能完整保留原函数的name
,doc
和args
,唯一有问题的就是查看对象的源码inspect.getsource(func)
返回的还是装饰器的源代码,需要改成inspect.getsource(func.__wrapped__)
使用wrapt实现的装饰器不需要担心之前inspect中遇到的所有问题,nspect.getsource(func)也准确无误。
import wrapt
# without argument in decorator
@wrapt.decorator
def logging(wrapped, instance, args, kwargs): # instance is must
print "[DEBUG]: enter {}()".format(wrapped.__name__)
return wrapped(*args, **kwargs)
@logging
def say(something): pass
使用wrapt只需要定义一个装饰器函数,但是函数签名是固定的,必须是(wrapped, instance, args, kwargs),注意第二个参数instance是必须的,就算不使用它。当装饰器装饰在不同位置时它将得到不同的值,比如装饰在类实例方法时可以拿到这个类实例。根据instance的值你能够更加灵活的调整装饰器。另外,args和kwargs也是固定的,注意前面没有星号。在装饰器内部调用原函数时才带星号。
使用wrapt写一个带参数的装饰器:
def logging(level):
@wrapt.decorator
def wrapper(wrapped, instance, args, kwargs):
print "[{}]: enter {}()".format(level, wrapped.__name__)
return wrapped(*args, **kwargs)
return wrapper
@logging(level="INFO")
def do(work): pass
wrapt
官方文档:http://wrapt.readthedocs.io/en/latest/quick-start.html
不能装饰@staticmethod
或者 @classmethod
,装饰器不能使用在静态方法和类方法上
Python的装饰器和Java的注解(Annotation)并不是同一回事,和C#中的特性(Attribute)也不一样,完全是两个概念。
装饰器的理念是对原函数、对象的加强,相当于重新封装,所以一般装饰器函数都被命名为wrapper()
,意义在于包装。函数只有在被调用时才会发挥其作用。比如@logging
装饰器可以在函数执行时额外输出日志,@cache
装饰过的函数可以缓存计算结果等等。
而注解和特性则是对目标函数或对象添加一些属性,相当于将其分类。这些属性可以通过反射拿到,在程序运行时对不同的特性函数或对象加以干预。比如带有Setup
的函数就当成准备步骤执行,或者找到所有带有TestMethod
的函数依次执行等等。
原文:https://www.cnblogs.com/huturen/p/14819808.html