威尼斯wns.9778官网活动_vnsc威尼斯城官网

热门关键词: 威尼斯wns.9778官网活动,vnsc威尼斯城官网
当前位置:威尼斯wns.9778官网活动 > 计算机教程 > 高逼格利器之Python闭包与装饰器,利器python

高逼格利器之Python闭包与装饰器,利器python

文章作者:计算机教程 上传时间:2019-05-10

开放:对拓展是开放的

    为什么要对扩展开放呢?

    我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。

封闭:对修改是封闭的

    为什么要对修改封闭呢?

    就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对其进行了修改,很有可能影响其他已经在使用该函数的用户。

装饰器完美的遵循了这个开放封闭原则。

例子:拷贝alex的网站登录

图片 1图片 2

  1 login_status = False
  2 def login(func):
  3     def login_inner(*args, **kwargs):
  4         _user = 'alex'
  5         _pwd = 'abc123'
  6         global login_status
  7         if login_status == False:
  8             user = input('User Name:')
  9             password = input('Password:')
 10             if user == _user and password == _pwd:
 11                 login_status = True
 12                 print('成功登录...')
 13             else:
 14                 print('用户名和密码错误')
 15         else:
 16             print('用户已经登录,验证通过')
 17         if login_status:
 18             func(*args, **kwargs)
 19     return login_inner
 20 
 21 def home():
 22     print('---首页---')
 23 def henan():
 24     print('---河南专区---')
 25 @login
 26 def hebei(type):
 27     print('---河北专区---', type)
 28 hebei = login(hebei)
 29 hebei("3p")

View Code

1.3 __closure__属性

在Python中,函数对象有一个__closure__属性,自由变量就被保存在函数对象的__closure__属性中

我们来验证这一点:

图片 3

1 def out_func(out_arg):
2     def in_func(in_arg):
3         return out_arg in_arg
4     return in_func
5 
6 add_10_counter = out_func(10)
7 print(add_10_counter.__closure__)
8 print(add_10_counter.__closure__[0].cell_contents)

View Code

结果:

(<cell at 0x0000000002589618: int object at 0x000000001E32EAF0>,)
10

看到了自由变量out_arg的值10;

这下明白了吧!当外部函数int_func被调用后,自由变量out_arg就会被保存在函数add_10_counter的__closure__属性中,所以add_10_counter可以使用该自由变量;

总结:

在Python中创建一个闭包可以归结为以下三点:

1)闭包函数必须被包裹在另一个函数中

2)闭包函数必须引用了上一级namespace中的变量

3)外部函数必须返回了该闭包函数

 上面关于闭包的介绍是为了介绍更重要的装饰器,下面就让我们来认识装饰器

  1. 运用装饰器遵循开放封闭原则

2.4 日常常见应用

1)分析、日志与手段

对于大型应用, 我们常常需要记录应用的状态,以及测量不同活动的数量。通过将这些特别的事件包装到函数或方法中,装饰器可以很轻松地满足这些需求,同时保证代码的可读性。

图片 4

 1 from myapp.log import logger
 2  
 3 def log_order_event(func):
 4     def wrapper(*args, **kwargs):
 5         logger.info("Ordering: %s", func.__name__)
 6         order = func(*args, **kwargs)
 7         logger.debug("Order result: %s", order.result)
 8         return order
 9     return wrapper
10  
11 @log_order_event
12 def order_pizza(*toppings):
13     # let's get some pizza!

View Code

2)验证以及运行时检查

Python 是一种强类型语言,但是变量的类型却是动态变化的。虽然这会带来很多好处,但是同时这也意味着更容易引入 bug。对于静态语言,例如 Java, 这些 bug 在编译阶段就可以被发现。因而,你可能希望在对传入或返回的数据进行一些自定义的的检查。装饰器就可以让你非常容易地实现这个需求,并一次性将其应用到多个函数上。

想像一下:你有许多函数,每个函数返回一个字典类型,该字典包含一个“summary ”域。这个域的值不能超过 80 个字符的长度。如果违反这个要求,那就是一个错误。下面这个装饰器会在错误发生时抛出 ValueError 异常;

图片 5

 1 def validate_summary(func):
 2     def wrapper(*args, **kwargs):
 3         data = func(*args, **kwargs)
 4         if len(data["summary"]) > 80:
 5             raise ValueError("Summary too long")
 6         return data
 7     return wrapper
 8  
 9 @validate_summary
10 def fetch_customer_data():
11     # ...
12  
13 @validate_summary
14 def query_orders(criteria):
15     # ...
16  
17 @validate_summary
18 def create_invoice(params):
19     # ...

View Code

3)创建框架

一旦你掌握了如何写装饰器,你就能够从其使用的简单的语法中获益颇丰,你可以为语言添加新的语义使其使用更加简单。接下来最棒的就是你可以自己扩展 Python 语法。

事实上,很多开源框架都是使用的这样的方式。 Web 应用框架 Flask 就是使用装饰器将不同 URL 路由到不同处理 HTTP 请求函数的:

图片 6

 1 # For a RESTful todo-list API.
 2 @app.route("/tasks/", methods=["GET"])
 3 def get_all_tasks():
 4     tasks = app.store.get_all_tasks()
 5     return make_response(json.dumps(tasks), 200)
 6  
 7 @app.route("/tasks/", methods=["POST"])
 8 def create_task():
 9     payload = request.get_json(force=True)
10     task_id = app.store.create_task(
11         summary = payload["summary"],
12         description = payload["description"],
13     )
14     task_info = {"id": task_id}
15     return make_response(json.dumps(task_info), 201)
16  
17 @app.route("/tasks/<int:task_id>/")
18 def task_details(task_id):
19     task_info = app.store.task_details(task_id)
20     if task_info is None:
21         return make_response("", 404)
22     return json.dumps(task_info)

View Code

这里有一个全局对象 app,此对象有一个 route 方法。此 route 函数返回一个用于修饰请求处理函数的装饰器。这背后的处理是非常复杂的,但是对于使用 Flask 的程序员来说,所有复杂的东西都被隐藏起来了。

 4)复用不能复用的代码

假设有一个古怪的 API。你可以通过 HTTP 发送 JSON 格式的请求,它 99.9% 的情况下都是正确工作的。但是,小部分请求会返回服务器内部错误的结果。这时候,你需要重新发送请求。在这种情况下,你需要实现重试逻辑,像这样:

图片 7

1 resp = None
2 while True:
3     resp = make_api_call()
4     if resp.status_code == 500 and tries < MAX_TRIES:
5         tries  = 1
6         continue
7     break
8 process_response(resp)

View Code

现在假设你的代码库中有很都地方都进行调用了函数  make_api_call,那么是不是需要在每个调用的地方都实现这个 loop 循环呢?是不是每次添加一次调用都要实现一遍这个循环呢?这种模式能难有一个样板代码,除非你使用装饰器,那么这就变得非常简单了:

图片 8

 1 # The decorated function returns a Response object,
 2 # which has a status_code attribute. 200 means
 3 # success; 500 indicates a server-side error.
 4  
 5 def retry(func):
 6     def retried_func(*args, **kwargs):
 7         MAX_TRIES = 3
 8         tries = 0
 9         while True:
10             resp = func(*args, **kwargs)
11             if resp.status_code == 500 and tries < MAX_TRIES:
12                 tries  = 1
13                 continue
14             break
15         return resp
16     return retried_func
17  
18 This gives you an easy-to-use @retry decorator:
19  
20 @retry
21 def make_api_call():
22     # ....

View Code

总结:

装饰器是面向过程编程的利器,学习装饰器是python最有价值的工具之一

http://www.bkjia.com/Pythonjc/1225124.htmlwww.bkjia.comtruehttp://www.bkjia.com/Pythonjc/1225124.htmlTechArticle高逼格利器之Python闭包与装饰器,利器python 生活在魔都的小明,终于攒够了首付,在魔都郊区买了一套房子;有一天,小明踩了狗屎,中了...

闭包:如果在一个内部函数里,对在外部作用域的变量(不是全局作用域)进行引用,那边内部函数被称为闭包(closure)

例如:如果在一个内部函数里:func2()就是内部函数,

对在外部作用域的变量进行引用:n就是被引用的遍历,n在外部作用域func里面被定义,而不是全局作用域

图片 9图片 10

  1 def func():
  2     n = 10
  3     def func2():
  4         print('func2', n)
  5     return func2
  6 
  7 f = func()
  8 f()
  9 

View Code

1.闭包

  1. 介绍装饰器

2.1 一个有趣的引子

初创公司有N个业务部门,1个基础平台部门,基础平台负责提供底层的功能,如:数据库操作、redis调用、监控API等功能。业务部门使用基础功能时,只需调用基础平台提供的功能即可。如下:

图片 11

 1 ############### 基础平台提供的功能如下 ###############
 2  
 3 def f1():
 4     print 'f1'
 5  
 6 def f2():
 7     print 'f2'
 8  
 9 def f3():
10     print 'f3'
11  
12 def f4():
13     print 'f4'
14  
15 ############### 业务部门A 调用基础平台提供的功能 ###############
16  
17 f1()
18 f2()
19 f3()
20 f4()
21  
22 ############### 业务部门B 调用基础平台提供的功能 ###############
23  
24 f1()
25 f2()
26 f3()
27 f4()

View Code

目前公司有条不紊的进行着,但是,以前基础平台的开发人员在写代码时候没有关注验证相关的问题,即:基础平台的提供的功能可以被任何人使用。现在需要对基础平台的所有功能进行重构,为平台提供的所有功能添加验证机制,即:执行功能前,先进行验证。

老大把工作交给 Low B,他是这么做的:

1)做法1

跟每个业务部门交涉,每个业务部门自己写代码,调用基础平台的功能之前先验证。诶,这样一来基础平台就不需要做任何修改了。

当天Low B 被开除了…

老大把工作交给 Low BB,他是这么做的:

2)做法2

图片 12

 1 ############### 基础平台提供的功能如下 ############### 
 2  
 3 def f1():
 4     # 验证1
 5     # 验证2
 6     # 验证3
 7     print 'f1'
 8  
 9 def f2():
10     # 验证1
11     # 验证2
12     # 验证3
13     print 'f2'
14  
15 def f3():
16     # 验证1
17     # 验证2
18     # 验证3
19     print 'f3'
20  
21 def f4():
22     # 验证1
23     # 验证2
24     # 验证3
25     print 'f4'
26  
27 ############### 业务部门不变 ############### 
28 ### 业务部门A 调用基础平台提供的功能### 
29  
30 f1()
31 f2()
32 f3()
33 f4()
34  
35 ### 业务部门B 调用基础平台提供的功能 ### 
36  
37 f1()
38 f2()
39 f3()
40 f4()

View Code

虽然业务部门不需要做任何改变,但是BB在每个基础平台函数都加入了相同的代码,没有考虑到代码复用,耦合,太低级了;

过了一周 Low BB 被开除了

老大把工作交给 Low BBB,他是这么做的:

3)做法3

只对基础平台的代码进行重构,其他业务部门无需做任何修改

图片 13

 1 ############### 基础平台提供的功能如下 ############### 
 2  
 3 def check_login():
 4     # 验证1
 5     # 验证2
 6     # 验证3
 7     pass
 8  
 9  
10 def f1():
11     
12     check_login()
13  
14     print 'f1'
15  
16 def f2():
17     
18     check_login()
19  
20     print 'f2'
21  
22 def f3():
23     
24     check_login()
25  
26     print 'f3'
27  
28 def f4():
29     
30     check_login()
31     
32     print 'f4'

View Code

BBB的想法和BB的是一样的,但是BBB考虑到了代码复用的问题;老大看了下Low BBB 的实现,嘴角漏出了一丝的欣慰的笑,语重心长的跟Low BBB聊了个天:

老大说:

写代码要遵循开放封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:

  • 封闭:已实现的功能代码块
  • 开放:对扩展开发

如果将开放封闭原则应用在上述需求中,那么就不允许在函数 f1 、f2、f3、f4的内部进行修改代码,老板就给了Low BBB一个实现方案:

4)最终方法

图片 14

 1 def w1(func):
 2     def inner():
 3         # 验证1
 4         # 验证2
 5         # 验证3
 6         func()
 7     return inner
 8  
 9 @w1
10 def f1():
11     print 'f1'
12 @w1
13 def f2():
14     print 'f2'
15 @w1
16 def f3():
17     print 'f3'
18 @w1
19 def f4():
20     print 'f4'

View Code

代码中的@w1就是一个装饰器;既没有对原基础平台函数f1(),f2(),f3(),f4()代码块进行修改,且实现了需求;

段子讲完了,让我们看看到底装饰器是什么,装饰器到底怎么实现上面功能的?

装饰器:本质上是一个闭包函数。

功能:在不修改原函数的及其调用方式的情况下对原函数进行功能扩展。这个特点正好满足软件开发原则:开放封闭原则

练习:请设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间:

图片 15图片 16

  1 import time
  2 import functools
  3 
  4 def metric(fn):
  5     @functools.wraps(fn)
  6     def wrapper(*args, **kwargs):
  7         start = time.time()
  8         ret = fn(*args, **kwargs)
  9         end = time.time()
 10         print('%s executed in %s ms' % (fn.__name__, end - start))
 11         return ret
 12     return wrapper
 13 
 14 @metric
 15 def fast(x, y):
 16     time.sleep(0.0012)
 17     return x   y
 18 
 19 @metric
 20 def slow(x, y, z):
 21     time.sleep(0.1234)
 22     return x * y * z
 23 
 24 f = fast(11, 22)
 25 s = slow(11, 22, 33)
 26 if f != 33:
 27     print('测试失败!')
 28 elif s != 7986:
 29     print('测试失败!')

View Code

2)功能是由一个out_func函数包裹了一个in_func函数实现的;当执行了add_10_counter

out_func(10)后,out_func(10)返回了一个函数对象in_func给add_10_counter,那么下次调用add_10_counter(x)时,实际上就是在调用in_func(x);但是我们看到in_func使用了变量out_arg,但是问题来了:out_arg即不在局部命名空间,也不在全局命名空间;为什么in_func可以使用out_arg呢?

根据闭包的定义,“如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。定义在外部函数内的但由内部函数引用或者使用的变量称为自由变量。”这样看,**in_func就是一个闭包函数,而out_arg就是那个自由变量;而且我们注意到外部函数out_func调用已经结束了,相应的内存空间也已经销毁了,但是自由变量out_arg却没有销毁回收,生命周期得到了延长,为什么呢?这里就涉及到函数的__closure__属性了;**

  1. 介绍闭包

2.3 装饰器高级应用

1)带参数的装饰器

图片 17

 1 '''带多参数的装饰器 '''
 2 
 3 from time import ctime,time
 4 
 5 def login(start_time_fuc, end_time_func):
 6     def wrapper(f):
 7         def hello_with_time(*args, **kwargs):
 8             #记录开始时间
 9             start_time_fuc()
10             res = f(*args, **kwargs)
11             #记录结束时间
12             end_time_func()
13             return res
14         return hello_with_time
15     return wrapper
16 
17 #记录执行的开始时间的函数
18 def start_time():
19     start = ctime(time())
20     print('start time: ', start)
21 
22 #记录执行的结束时间的函数
23 def end_time():
24     end = ctime(time())
25     print('end time: ', end)
26 
27 #带参数的装饰器,而且参数还是函数对象
28 @login(start_time, end_time)
29 def hello(name):
30     print('hello', name)
31 
32 #调用hello函数
33 hello('world')

View Code

上例中,执行到:

@login(start_time, end_time)
def hello

解释器解释成:

hello = login(start_time, end_time)(hello)

执行了两个函数,相当于

temp_f = login(start_time, end_time)
hello =temp_f(hello)

注意不同参数对应的不同的位置;

2)多个装饰器

图片 18

 1 '''多个装饰器'''
 2 
 3 from time import ctime,time
 4 
 5 #增加记录开始时间的功能
 6 def login_start(start_time_fuc):
 7     def wrapper(f):
 8         def hello_with_time(*args, **kwargs):
 9             #记录开始时间
10             start_time_fuc()
11             res = f(*args, **kwargs)
12             return res
13         return hello_with_time
14     return wrapper
15 
16 #增加记录结束时间的功能
17 def login_end(end_time_fuc):
18     def wrapper(f):
19         def hello_with_time(*args, **kwargs):
20             res = f(*args, **kwargs)
21             #记录结束时间
22             end_time_fuc()
23             return res
24         return hello_with_time
25     return wrapper
26 
27 #记录执行的开始时间的函数
28 def start_time():
29     start = ctime(time())
30     print('start time: ', start)
31 
32 #记录执行的结束时间的函数
33 def end_time():
34     end = ctime(time())
35     print('end time: ', end)
36 
37 #多个装饰器,第一次增加记录开始时间的功能,第二次增加记录结束时间的功能
38 @login_end(end_time)
39 @login_start(start_time)
40 def hello(name):
41     print('hello', name)
42 
43 #调用hello函数
44 hello('world')

View Code

上例中,当执行到:

@login_end(end_time)
@login_start(start_time)
def hello

解释为:

hello = login_end(end_time)(login_start(start_time)(hello))

看上去有点晕,实际上你就认为加了两次功能,第一次加功能使用了装饰器@login_start,第二次加功能用了装饰器@login_end;

本文由威尼斯wns.9778官网活动发布于计算机教程,转载请注明出处:高逼格利器之Python闭包与装饰器,利器python

关键词: