python 初次认识with 和 contextlib.closing

本文深入探讨Python的contextlib模块,讲解如何使用with语句管理资源,包括文件、数据库连接和网络请求。通过实例演示contextlib.closing和contextlib.contextmanager的使用,以及它们在简化代码和提高效率方面的作用。

https://blog.youkuaiyun.com/emaste_r/article/details/78105713

简单介绍下我认识contextlib的过程吧,觉得这个内置lib还挺有意思的。

1、
之前的我,只知道with会用来关闭文件,数据库资源,这很好。
只要实现了__enter__() 和 exit()这两个方法的类都可以轻松创建上下文管理器,就能使用with。

2、
我打开两个数据库的时候,都是

with xxx as conn1:
with yyy as conn2:
code
真是蠢如老狗呀,其实可以:
with xxx as conn1, yyy as conn2:
code

3、
总感觉离开了with block,语句体的资源(文件啊,数据库连接啊,网络请求呀)就会自动关闭。
可是有一天我看到contextlib.closing()。 一脸懵逼,有了with还要这个干嘛,这是我内心真实OS。。
from contextlib import closing
from urllib2 import urlopen

with closing(urlopen(‘http://www.python.org’😉) as page:
for line in page:
print(line)
先来否定我的想法,凡用with就万事大吉,自动帮我关闭。
class Door(object):
def open(self):
print ‘Door is opened’

def close(self):
    print 'Door is closed'

with Door() as d:
d.open()
结果:

报错:

Traceback (most recent call last):
File “1.py”, line 38, in
with Door() as d:
AttributeError: exit
果然呢,因为with语句体执行之前运行__enter__方法,在with语句体执行完后运行__exit__方法。
如果一个类如Door连这两个方法都没有,是没资格使用with的。

4、
好吧,正式认识下contextlib:https://docs.python.org/dev/library/contextlib.html
有些类,并没有上述的两个方法,但是有close(),能不能在不加代码的情况下,使用with呢?
可以:
class Door(object):
def open(self):
print ‘Door is opened’

def close(self):
    print 'Door is closed'

with contextlib.closing(Door()) as door:
door.open()
结果:
Door is opened
Door is closed
contextlib.closing(xxx),原理如下:
class closing(object):
“”“Context to automatically close something at the end of a block.
Code like this:
with closing(.open()) as f:

is equivalent to this:
f = .open()
try:

finally:
f.close()
“””
def init(self, thing):
self.thing = thing
def enter(self):
return self.thing
def exit(self, *exc_info):
self.thing.close()
这个contextlib.closing()会帮它加上__enter__()和__exit__(),使其满足with的条件。

5、
是不是只有类才能享受with的便利呀? 我单单一个方法行不行?
行!既然认识了contextlib.closing(),必须认识下contextlib.contextmanager
这是一个装饰器,可以让一个func()变成一个满足with条件的类实例…

!!!这个func()必须是生成器…
yield前半段用来表示__enter__()
yield后半段用来表示__exit__()
from contextlib import contextmanager

@contextmanager
def tag(name):
print("<%s>" % name)
yield
print("</%s>" % name)

with tag(“h1”):
print ‘hello world!’
结果:

hello world!

Wow,这个还真的挺酷的,以后可以用这个contextmanager来实现一些装饰器才能做的事,
比如给一段代码加时间cost计算:
装饰器版本:
import time
def wrapper(func):
def new_func(*args, **kwargs):
t1 = time.time()
ret = func(*args, **kwargs)
t2 = time.time()
print ‘cost time=’, (t2-t1)
return ret
return new_func

@wrapper
def hello(a,b):
time.sleep(1)
print 'a + b = ', a+b

hello(100,200)
结果:
a + b = 300
cost time= 1.00243401527

contextmanger版本:
from contextlib import contextmanager

@contextmanager
def cost_time():
t1 = time.time()
yield
t2 = time.time()
print ‘cost time=’,t2-t1

with cost_time():
time.sleep(1)
a = 100
b = 200
print 'a + b = ', a + b
结果:
a + b = 300
cost time= 1.00032901764
当然还是用装饰器方便美观点啦~

这是contextmanager原理:
1、因为func()已经是个生成器了嘛,所以运行__enter__()的时候,contextmanager调用self.gen.next()会跑到func的yield处,停住挂起,这个时候已经有了t1=time.time()
2、然后运行with语句体里面的语句,也就是a+b=300
3、跑完后运行__exit__()的时候,contextmanager调用self.gen.next()会从func的yield的下一句开始一直到结束。这个时候有了t2=time.time(),t2-t1从而实现了统计cost_time的效果,完美。
源码:
class GeneratorContextManager(object):
“”“Helper for @contextmanager decorator.”""

def __init__(self, gen):
    self.gen = gen

def __enter__(self):
    try:
        return self.gen.next()
    except StopIteration:
        raise RuntimeError("generator didn't yield")

def __exit__(self, type, value, traceback):
    if type is None:
        try:
            self.gen.next()
        except StopIteration:
            return
        else:
            raise RuntimeError("generator didn't stop")
    else:
        if value is None:
            # Need to force instantiation so we can reliably
            # tell if we get the same exception back
            value = type()
        try:
            self.gen.throw(type, value, traceback)
            raise RuntimeError("generator didn't stop after throw()")
        except StopIteration, exc:
            return exc is not value
        except:
            if sys.exc_info()[1] is not value:
                raise

def contextmanager(func):
@wraps(func)
def helper(*args, **kwds):
return GeneratorContextManager(func(*args, **kwds))
return helper

<think>好的,我现在要回答用户关于Pythoncontextlib.suppress(ValueError)的作用的问题。首先,我需要确认contextlib.suppress的功能是什么。记得suppress是一个上下文管理器,用于抑制特定的异常。用户想知道它的用法意义,特别是针对ValueError的情况。 首先,我应该解释contextlib.suppress的基本作用。它允许在代码块中忽略指定的异常,这样当这些异常被抛出时,不会导致程序中断,而是继续执行后面的代码。例如,如果用户想忽略处理文件时可能出现的FileNotFoundError,就可以用suppress(FileNotFoundError)来包裹代码。 接下来,用户具体问的是ValueError的情况。我需要举一个例子,说明在什么情况下会用到抑制ValueError。比如,当用户尝试将字符串转换为整数时,如果字符串无法转换,就会引发ValueError。使用suppress后,可以跳过这些错误的情况,继续处理其他数据。 然后,我需要对比传统的try-except写法。传统方法需要在except块中写pass,而使用suppress可以让代码更简洁,尤其是在只需要忽略异常而不需要处理的时候。这里可能需要引用相关文档或例子,比如Python官方文档中的说明,或者常见的用法场景。 还要注意,用户可能想知道使用suppress时是否会影响其他异常。需要强调,只有指定的异常会被抑制,其他异常仍会正常抛出。例如,在示例中,如果出现除零错误或者其他异常,仍然会中断程序,而只有ValueError被忽略。 此外,可能需要提到一些使用场景,比如数据清洗时忽略无法转换的数据,或者处理不确定的输入时避免程序崩溃。同时,要提醒用户不要过度使用异常抑制,以免隐藏潜在的问题。 现在检查是否有相关的引用内容。用户提供的引用中,引用[1]提到在列表排序时修改列表会引发ValueError,但不确定是否直接相关。引用[2][3]涉及其他模块的异常处理,可能不需要在此处引用。因此,可能不需要添加这些引用标记,或者仅在必要时提及。 最后,确保回答结构清晰,分点说明用法意义,并给出示例代码。同时,生成相关问题,帮助用户进一步了解该主题。</think>### Python中`contextlib.suppress(ValueError)`的作用 `contextlib.suppress(ValueError)` 是Python标准库`contextlib`提供的一个上下文管理器,**用于临时抑制指定类型的异常**。当代码块中抛出`ValueError`时,该异常会被自动捕获并忽略,程序继续执行后续代码。 --- #### 1. 核心用法 通过`with`语句包裹可能抛出异常的代码块,显式声明需要忽略的异常类型: ```python from contextlib import suppress with suppress(ValueError): # 可能触发ValueError的操作 int("invalid_string") # 转换失败会触发ValueError,但此处被抑制 print("程序继续执行") # 即使异常发生,仍会执行到此处 ``` --- #### 2. 与传统写法的对比 传统`try-except`需显式处理异常,而`suppress`简化了代码: ```python # 传统写法 try: int("abc") except ValueError: pass # 使用suppress with suppress(ValueError): int("abc") ``` --- #### 3. 典型应用场景 - **数据清洗**:忽略无法转换的无效数据 ```python data = ["42", "NaN", "100"] valid_numbers = [] with suppress(ValueError): for s in data: valid_numbers.append(int(s)) # 跳过"NaN"的转换错误 ``` - **资源清理**:忽略文件删除时的`FileNotFoundError` ```python with suppress(FileNotFoundError): os.remove("temp_file.txt") ``` --- #### 4. 注意事项 - **精确控制范围**:仅抑制预期内的异常类型,避免隐藏其他潜在错误 ```python with suppress(ValueError): x = 1 / 0 # ZeroDivisionError仍会触发,未被抑制 ``` - **Python官方建议**:`suppress`适用于明确需要忽略异常的场景[^1]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值