zoukankan      html  css  js  c++  java
  • Python学习札记(二十六) 函数式编程7 修饰器

    修饰器

    NOTE

    1.函数对象有一个__name__属性,可以拿到函数的名字:

    #!/usr/bin/env python3
    
    def now():
    	print('2017/2/19')
    
    def main():
    	f = now
    	f()
    	print(now.__name__)
    	print(f.__name__)
    
    if __name__ == '__main__':
    	main()
    
    sh-3.2# ./decorator1.py 
    2017/2/19
    now
    now
    

    2.增强now函数的功能,又不想重新对now函数进行定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。

    本质上,装饰器就是一个返回函数的高阶函数:

    #!/usr/bin/env python3
    
    def log(func):
    	def wrapper():
    		print('call %s():' % func.__name__)
    		return func()
    	return wrapper
    
    @log
    def now():
    	print('2017/2/19')
    
    def main():
    	f = now
    	f()
    	print(now.__name__)
    	print(f.__name__)
    
    if __name__ == '__main__':
    	main()
    
    sh-3.2# ./decorator1.py 
    call now():
    2017/2/19
    wrapper
    wrapper
    

    把@log放到now()函数的定义处,相当于执行了语句:

    now = log(now)
    

    由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数

    now -> now() 
    
    => 
    
    now -> wrapper()
    

    把函数名看成指向函数的指针变量就好了。

    3.将wrapper()函数的参数定义修改为(*args, **kw),使wrapper()函数可以接受任意参数的调用。

    4.如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。

    eg.使用Flask web框架的时候,需要传入参数(路径)制定路由。

    @app.route('/')
    def index()
        return '<h1>Hello</h1>'
    

    这种传入参数的实现是三个函数的嵌套:

    def log(text):
    	def decorator(func):
    		def wrapper(*args, **kw):
    			print('%s %s()' % (text, func.__name__))
    			return func(*args, **kw)
    		return wrapper
    	return decorator
    
    @log('wasdns')
    def now():
    	print('2017/2/19')
    
    wasdns now()
    2017/2/19
    wrapper
    wrapper
    

    @log('wasdns')

    now = log('wasdns')(now)
    

    首先执行log('wasdns'),返回的是decorator函数,再调用返回的函数,参数是now函数,返回值最终是wrapper函数。

    一层一层解开。

    5.函数也是Object,它有__name__等属性,但是在上面折腾之后,它的__name__属性变为:

    wrapper
    

    避免有些依赖函数签名的代码执行出错 => 需要把原始函数的__name__等属性复制到wrapper()函数中 => 调用Python内置的functools.wraps。

    def log(func):
    	@functools.wraps(func)
    	def wrapper(*args, **kw):
    		print('call %s():' % func.__name__)
    		return func(*args, **kw)
    	return wrapper
    
    sh-3.2# ./decorator1.py 
    call now():
    2017/2/19
    now
    now
    
    def log(text):
    	def decorator(func):
    		@functools.wraps(func)
    		def wrapper(*args, **kw):
    			print('%s %s()' % (text, func.__name__))
    			return func(*args, **kw)
    		return wrapper
    	return decorator
    
    sh-3.2# ./decorator2.py 
    wasdns now()
    2017/2/19
    now
    now
    

    加在最里层的函数首部(最后返回的是最里层的函数),修改其属性就达到了我们的目的。

    decorator可以增强函数的功能,定义起来虽然有点复杂,但使用起来非常灵活和方便。

    Practice

    请编写一个decorator,能在函数调用的前后打印出'begin call'和'end call'的日志。

    #!/usr/bin/env python3
    
    import functools
    
    def log1(func):
    	print('begin call')
            func()
    	def wrapper(*args, **kw):
    		print('end call')
    	return wrapper
    
    @log1
    def func():
    	print('Hey Girl')
    
    def main():
    	func()
    
    if __name__ == '__main__':
    	main()
    
    sh-3.2# ./decorator3.py 
    begin call
    Hey Girl
    end call
    

    再思考一下能否写出一个@log的decorator,使它既支持:

    @log
    def f():
        pass
    
    

    又支持:

    @log('execute')
    def f():
        pass
    

    2017/2/19

  • 相关阅读:
    进入全屏 nodejs+express+mysql实现restful风格的增删改查示例
    WebAPI 实现前后端分离
    android 集成支付宝app支付(原生态)-包括android前端与java后台
    Windows 64 位系统下 Python 环境的搭建
    Es6主要特征详解
    js上传图片
    Python socket
    设置windows开机自启某个软件
    oracle导入导出数据
    mysql触发器,答题记录表同步教学跟踪(用户列表)
  • 原文地址:https://www.cnblogs.com/qq952693358/p/6417509.html
Copyright © 2011-2022 走看看