python装饰器、with和contextmanager

1,装饰器: 

装饰器是一种简单的记号而已,有如下等价的转换规则(带不带参数的装饰器都一样,只要是一个可调用对象即可)。

@foo

def bar:

    pass

等价于:

x = foo

bar = x(bar)

'----------------'

@foo('abc')

def bar:

    pass

等价于:

x = foo('abc')

bar = x(bar)

'----------------'

@foo.X('abc').Y

def bar:

    pass

等价于:

x = foo.X('abc').Y

bar = x(bar)

真正起作用的是"函数式编程"方面的知识,包括闭包,LEGB访问规则,自由变量等。

装饰器的用处:返回一个增强版的函数,主要是对函数的上文和下文,都可以进行处理。

比如:参数检查,缓存数据(建立输入与输出的字典),代理,上下文提供者

from threading import RLock
lock = RLock()
def locker(function):
    def _locker(*args,**kw):
        lock.acquire()
        try:
            return function(*args,**kw)
        finally:
            lock.release()
    return _locker

@locker
def thread_safe():
    pass
以上装饰器的定义都没有问题,但还差最后一步!!

因为我们讲了函数也是对象,它有__name__等属性,但经过decorator装饰之后的函数,

它们的__name__已经从原来的'thread_safe'变成了'_locker'。所以不完美。

不需要直接编写wrapper.__name__ = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的。

import functools
def locker(function):
    @functools.wraps(function)
    def _locker(*args,**kw):
       lock.acquire()
       try:
           return function(*args,**kw)
       finally:
           lock.release()
    return _locker

2,with语句:

任何实现了 __enter__和__exit__的类都可以和 with使用。
class Context(object):
    def __enter__(self):
        print 'entering the zone'
    def __exit__(self, exception_type, exception_value, 
                exception_traceback):
        print 'leaving the zone'
        if exception_type is None:
            print 'with no error'
        else:
            print 'with an error (%s)' % exception_value
        '异常是会被新的异常覆盖的
        raise TypeError('i am the second bug')  
try:
    with Context():
        print 'i am the buggy zone'
        raise TypeError('i am the bug')
except Exception,s:
    print 'I got :',Exception,s

3,contextmanager装饰器:

一种和yield结合使用的方便的上下文管理方式,切记yield是协程技术。

Typical usage: 
    # @contextmanager 
    def some_generator(<arguments>): 
        <setup> 
        try: 
            yield <value> 
        finally: 
            <cleanup> 

This makes this: 
    with some_generator(<arguments>) as <variable>: 
        <body> 

equivalent to this: 
    <setup> 
    try: 
        <variable> = <value> 
        <body> 
    finally: 
        <cleanup>
'---------自己实现contextmanager装饰器---------------'
class MyGeneratorContextManager(object):
    def __init__(self, gen):
        print("__init__ called")
        self.gen = gen
    def __enter__(self):
        print("__enter__ called")
        return self.gen.next()
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("__exit__called exc_type = %s, exc_val = %s, exc_tb = %s"\
              % (exc_type, exc_val, exc_tb))
        try:
            ' 即便不调用next也是没有关系的,只是不会做清理的处理,不会执行print("end foo"),但是finally还是程序结束前总会被执行的'
            return self.gen.next() 
        except StopIteration: 
            '是不会捕获with中的异常,但是这里抛出的异常可以覆盖with中抛出的异常!'
            print 'nothing to do with StopIteration'
        
def MyContextManager(func):
    def _deco(*args,**kwargs):
        print("func info:", func)
        return MyGeneratorContextManager(func(*args,**kwargs))
    return _deco

@MyContextManager
def foo(*args,**kwargs):
    try: # 尝试用老方法捕捉错误
        print("start foo:", args,kwargs)
        yield [1, 2, 3]
        print("end foo") # 需要调用self.gen.next()才能输出
    except (Exception, AssertionError):
        print("EXCEPTION ENCOUNTERED!")
    finally:
        print("FINALLY")

with foo("oh no!") as tmp: # tmp的值就是__enter__返回的值
    print("START WITH")
    print 'tmp:',tmp
    assert 1>2
    # 出错之后直接从with中跳出去,下面不可能被执行
    print("END WITH")

4,多个装饰器

#coding:utf-8  
def decorator1(func):  
	print 1
	def wrapper():  
		print 2
		func()
		print 3
	print 4		
	return wrapper  

def decorator2(func):  
	print 5
	def wrapper():  
		print 6       
		func()
		print 7	   
	print 8  
	return wrapper  
 
@decorator1  
@decorator2  
def test():  
	print 'hello python!'  
  
test()  # 等价于下面两行代码
# test = decorator2(test)
# test = decorator1(test)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值