高阶Python一学就会
在前一篇文章中,我们学习了几个一般来说比较有用的Python语言的特性。 考虑到这篇文章是前一篇文章的续集,在这里我们进一步延伸一些显式使用装饰器的概念,我们并没有扰乱前一篇文章的内容。
装饰器
装饰器的概念展现了python领域内最漂亮和最强大的设计可能性之一,这不仅仅是在Python编程中,也在整个软件设计领域。本质上来说,装饰器就是一种包装,主要是想在不改变被包装的原代码的原则下,实现延伸代码功能性的目的。为了能让这个概念更清晰易懂,我们来从最基础的内容开始。
函数亦称为第一类对象
函数简单来说就是基于给定的自变量返回一个值。在python中,这些函数还有另外一种荣誉称号,叫作第一类对象。考虑到函数可以被像普通的对象一样被作为自变量传递,函数荣膺这项称号还真的是恰如其分。比如说,它们可以被作为自变量传递给其它函数,同时也可以被用做一个函数返回值。
作为自变量的函数
def greet(name):
print ('Hello ' + name)
def send_greetings(fun, name):
fun(name)
send_greetings(greet, 'John')
闭包函数
闭包函数是定义在其它函数内部的函数。这使得:闭包函数只有在其父函数被调用时才会被定义,或者说闭包函数的作用域仅存在于父函数内部,亦或者说闭包函数只是作为父函数的一个局域变量存在的。
def send_greetings(name):
def greet_message():
return ‘Hello ‘
result = greet_message() + name
print (result)
send_greetings(‘John’)
从函数中返回函数
Python还允许你将一个函数作为返回值传给另一个函数。本质上来讲,我们只是将内部函数的引用传回以待后续的调用。
def classify(element):
def even_number():
print ('Element is even.')
def odd_number():
print ('Element is odd.')
if element%2 == 0:
return even_number
else:
return odd_number
classify(2)()
装饰器
现在,有了上面所有这些基本概念的经验之后,我们来这些概念串起来组成一个完整的图像。
def my_decorator(fun):
def wrapper():
print (‘Before calling the function…’)
fun()
print (‘After calling the function…’)
return wrapper
def say_hello():
print (‘Hello!’)
say_hello = my_decorator(say_hello)
就是这样!!! 这就是我们能得到的最简单的装饰器。我们把到前面学到的东西一个个的都用进来了。所以装饰器其实就是
一个把另一个函数作为自变量的函数,它生成了一个新的函数,同时对原先的函数扩展了功能性,它最终返回的是生成的新的函数,这样我们可以在其他地方使用它。
另外,python让编程者可以非常整洁漂亮地创建并使用装饰器。
def my_decorator(fun):
def wrapper():
print (‘Before calling the function…’)
fun()
print (‘After calling the function…’)
return wrapper
@my_decorator
def say_hello():
print (‘Hello!’)
上下文环境管理
简单来说吧,上下文管理是一种资源的获取和释放机制,它避免了资源泄露并确保即使在遇到一些糟糕的异常情况后仍能完成恰当的清理工作。比如说,保证文件在打开之后的关闭,锁在获得之后的释放。这个概念在大量其他编程语言中有清楚地表述和恰当地使用,比如说C++ 中的RAII。
技术上来说,它是一个对象需要去遵循的通讯协议。这个协议要求,对象要像一个上下文管理器一样,要执行 __enter__
和 __exit__
方法。
__enter__
返回要被管理的资源,而__exit__
完成任何可能出现的清理工作并且什么也不返回。
class File:
def __init__(self, name):
self.name = name
def __enter__(self):
self.file = open(self.name, 'w')
return self.file
def __exit__(self, type, value, trace_back):
if self.file:
self.close()
现在,上面这个类可以在with
语句中被安全地使用。更一般地,使用with
,我们可以调用任何东西并返回一个上下文管理器。
with File('example.txt') as f:
f.write('Hey hello')
f.write('See you later. Bye!!!')
__enter__
在with
语句被调用的时候被执行。当代码块的语句执行完了的时候 __exit__
方法会被调用。
上下文管理器还可以被用在更复杂的问题中。我们看另一个上下文管理器几乎不可避免的例子。这个问题中的资源就是锁,我们可以避免的问题就是死锁。
from threading import Lock
lock = Lock()
def do_something():
lock.acquire()
raise Exception('Oops I am sorry. I have to raise it!')
lock.release()
try:
do_something()
except:
print ('Got an exception.')
注意,在释放锁之前会报异常。这会导致一个明显的副作用,那就是所有调用do_something
的其它线程会永远地出现拥堵从而导致系统死锁。使用上下文管理器,我们可以避免这种尴尬的情况。
from threading import Lock
lock = Lock()
def do_something():
with lock:
raise Exception('Oops I am sorry. I have to raise it!')
try:
do_something()
except:
print ('Got an exception.')
哇塞!看到没,即使遇到一些异常情况,代码也能恰当地完成清理工作。很明显,在任何可能的情况下都不会出现使用了上下文管理器来获得锁,而还没有释放锁就结束的情形。它就应该是这样。
连锁的异常
设想一种情况,因为除以零而使得方法抛出ZeroDivisionError
的异常。显然,在python中我们有很好的一组工具能够来处理异常。
然而,如果异常的原因是因为这个函数所调用的一些其它函数中出现了TypeError
呢。仅仅报告最顶层的异常是不够,这样我们会丢失原始的异常信息和一连串异常的主要原因。
为了演示这个问题中的概念,我们举个简单的例子。
def chained_exceptions():
try:
raise ValueError(17)
except Exception as ex:
raise ValueError(23) from ex
if __name__ == "__main__":
chained_exceptions()
在python 2.0中,上述情况会导致后面的异常被抛出而前面的会丢失,像下面这样。
Traceback (most recent call last):
File “test.py”, line 3, in chained_exceptions
raise ValueError(23)
ValueError: 23
很明显,我们丢失了信息中有价值的那部分,因为我们丢失了异常导致的真正的起因。然而,在python3.0中运行同样的脚本,我们可以看到完整的异常栈跟踪的信息。
Traceback (most recent call last):
File “test.py”, line 3, in chained_exceptions
raise ValueError(17)
ValueError: 17
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File “test.py”, line 8, in <module> chained_exceptions()
File “test.py”, line 5, in chained_exceptions
raise ValueError(23) from ex
ValueError: 23
如果对您对本文的内容有任何的修改或者改进的意见,请在评论中让我看到。
(原文链接:https://medium.com/quick-code/advanced-python-made-easy-2-d5a7ffb4e658)