python中装饰器的理解
- 2020-09-22 14:05:00
- 六月
- 来源:
- https://www.runoob.com/w3cnote/python-func-decorators.html
- 转贴 754
1、基础知识
a.一般函数定义
def hi(name="yasoob"): return "hi " + name print(hi()) # output: 'hi yasoob' # 我们甚至可以将一个函数赋值给一个变量,比如 greet = hi # 我们这里没有在使用小括号,因为我们并不是在调用hi函数 # 而是在将它放在greet变量里头。我们尝试运行下这个 print(greet()) # output: 'hi yasoob' # 如果我们删掉旧的hi函数,看看会发生什么! del hi print(hi())#outputs: NameError print(greet())#outputs: 'hi yasoob'
b.函数中定义函数,函数嵌套 (闭包)
def hi(name="yasoob"): print("now you are inside the hi() function") def greet(): return "now you are in the greet() function" def welcome(): return "now you are in the welcome() function" print(greet()) print(welcome()) print("now you are back in the hi() function") hi() #output:now you are inside the hi() function #now you are in the greet() function #now you are in the welcome() function #now you are back in the hi() function # 上面展示了无论何时你调用hi(), greet()和welcome()将会同时被调用。 # 然后greet()和welcome()函数在hi()函数之外是不能访问的,比如: greet() #outputs: NameError: name 'greet' is not defined
c.函数中返回函数
def hi(name="yasoob"): def greet(): return "now you are in the greet() function" def welcome(): return "now you are in the welcome() function" if name == "yasoob": return greet else: return welcome a = hi() print(a) #outputs: <function greet at 0x7f2143c01500> #上面清晰地展示了`a`现在指向到hi()函数中的greet()函数 #现在试试这个 print(a()) #outputs: now you are in the greet() function
d.将函数作为参数传递给函数
def hi(): return "hi yasoob!" def doSthBeforeHi(func): print("I am doing some boring work before executing hi()") print(func()) doSthBeforeHi(hi) #outputs:I am doing some boring work before executing hi() # hi yasoob!
2、装饰器实现
a.简单的装饰器
def debug(func): def wrapper(): print('[debug]:enter {}()'.format(func.__name__)) func() return func return wrapper @debug def say_hello(): print('hello') #say_hello = debug(say_hello) # 添加功能并保持原函数名不变,可以使用@语法糖实现同样功能(@debug) say_hello()
b.带参数的装饰器
指定装饰器函数接受和原函数一样的参数,解决被装饰函数的参数传递问题
def debug(func): def wrapper(sth): # 指定和被包装函数一样的参数 print "[DEBUG]: enter {}()".format(func.__name__) return func(sth) return wrapper # 返回包装过函数 @debug def say(sth): print("hello {}!".format(sth))
使用可变参数*args和关键字参数**kwargs,让装饰器就可以用于任意目标函数
def debug(func): def wrapper(*args, **kwargs): #接受任意类型的参数 print("[DEBUG]: enter {}()".format(func.__name__)) print("Prepare and say...",) return func(*args, **kwargs) return wrapper # 返回 @debug def say(something): print "hello {}!".format(something)
3、函数装饰器,基于类实现的装饰器
def logging(level): def wrapper(func): def inner_wrapper(*args, **kwargs): print "[{level}]: enter function {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 = logging(level='INFO')(say) @logging(level='DEBUG') def do(something): print("do {}...".format(something)) if __name__ == '__main__': say('hello') do("my work")
基于类实现的装饰器:装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。在Python中一般callable对象都是函数,但也有例外。只要某个对象重载了__call__()方法,那么这个对象就是callable的。
4、@property装饰器,@staticmethod装饰器,@classmethod装饰器,property()函数
内置的装饰器和普通的装饰器原理是一样的,只不过返回的不是函数,而是类对象,所以更难理解一些。
@property:
@property 语法糖提供了比 property() 函数更简洁直观的写法。 被 @property 装饰的方法是获取属性值的方法,被装饰方法的名字会被用做 属性名。 被 @属性名.setter 装饰的方法是设置属性值的方法。 被 @属性名.deleter 装饰的方法是删除属性值的方法。 class Student: def __init__(self): self._age = None @property def age(self): print('获取属性时执行的代码') return self._age @age.setter def age(self, age): print('设置属性时执行的代码') self._age = age @age.deleter def age(self): print('删除属性时执行的代码') del self._age student = Student() # 设置属性 student.age = 18 # 获取属性 print('学生年龄为:' + str(student.age)) # 删除属性 del student.age
property():
property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
说明:
fget 是获取属性值的方法。
fset 是设置属性值的方法。
fdel 是删除属性值的方法。
doc 是属性描述信息。如果省略,会把 fget 方法的 docstring 拿来用(如果有的话)
class Student: def __init__(self): self._age = None def get_age(self): print('获取属性时执行的代码') return self._age def set_age(self, age): print('设置属性时执行的代码') self._age = age def del_age(self): print('删除属性时执行的代码') del self._age age = property(get_age, set_age, del_age, '学生年龄') student = Student() # 注意要用 类名.属性.__doc__ 的形式查看属性的文档字符串 print('查看属性的文档字符串:' + Student.age.__doc__) # 设置属性 student.age = 18 # 获取属性 print('学生年龄为:' + str(student.age)) # 删除属性 del student.age
class People: def __init__(self, name, age): self.__name = name self.__age = age def get_age(self): return self.__age def set_age(self, age): if isinstance(age, int): self.__age = age else: raise ValueError def del_age(self): print("删除年龄数据!") # 核心在这句 age = property(get_age, set_age, del_age, "年龄") obj = People("jack", 18) print(obj.age) obj.age = 19 print("obj.age: ", obj.age) del obj.age
通过语句age = property(get_age, set_age, del_age, "年龄")将一个方法伪装成为属性。其效果和装饰器的方法是一样的。
property()函数的参数:
第一个参数是方法名,调用 实例.属性 时自动执行的方法
第二个参数是方法名,调用 实例.属性 = XXX时自动执行的方法
第三个参数是方法名,调用 del 实例.属性 时自动执行的方法
第四个参数是字符串,调用 实例.属性.__doc__时的描述信息。
参考: https://www.runoob.com/w3cnote/python-func-decorators.html
https://www.cnblogs.com/cicaday/p/python-decorator.html
https://www.jb51.net/article/168276.htm
https://www.liujiangblog.com/course/python/46
https://www.cnblogs.com/yangzhen-ahujhc/p/12300189.html