众所周知,python __del__方法不应该用于清理重要的东西,因为不能保证调用此方法.另一种方法是使用上下文管理器,如几个线程中所述.
但我不太明白如何重写一个类来使用上下文管理器.详细说明,我有一个简单的(非工作)示例,其中包装器类打开并关闭设备,并且在任何情况下,类的实例都超出其范围(异常等)时将关闭设备.
第一个文件mydevice.py是打开和关闭设备的标准包装类:
class MyWrapper(object):
def __init__(self, device):
self.device = device
def open(self):
self.device.open()
def close(self):
self.device.close()
def __del__(self):
self.close()
Run Code Online (Sandbox Code Playgroud)
这个类由另一个类使用myclass.py:
import mydevice
class MyClass(object):
def __init__(self, device):
# calls open in mydevice
self.mydevice = mydevice.MyWrapper(device)
self.mydevice.open()
def processing(self, value):
if not value:
self.mydevice.close()
else:
something_else()
Run Code Online (Sandbox Code Playgroud)
我的问题:当我mydevice.py用with __enter__和__exit__方法实现上下文管理器时,如何处理这个类myclass.py?我需要做点什么
def __init__(self, device):
with mydevice.MyWrapper(device):
???
Run Code Online (Sandbox Code Playgroud)
但是如何处理呢?也许我忽略了一些重要的事情?或者我可以仅在函数内使用上下文管理器而不是作为类范围内的变量?
我有timeout与信号完美配合的上下文管理器,但它在多线程模式下引发错误,因为信号只在主线程中起作用.
def timeout_handler(signum, frame):
raise TimeoutException()
@contextmanager
def timeout(seconds):
old_handler = signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(seconds)
try:
yield
finally:
signal.alarm(0)
signal.signal(signal.SIGALRM, old_handler)
Run Code Online (Sandbox Code Playgroud)
我见过装饰器的实现,timeout但我不知道如何传递yield内部派生的类threading.Thread.我的变体不起作用.
@contextmanager
def timelimit(seconds):
class FuncThread(threading.Thread):
def run(self):
yield
it = FuncThread()
it.start()
it.join(seconds)
if it.isAlive():
raise TimeoutException()
Run Code Online (Sandbox Code Playgroud) 从上下文管理器上的datamodel文档:
请注意,
__exit__()方法不应该重新加入传入的异常; 这是来电者的责任.
我有一个临时文件,我想要释放它的文件描述符,close但没有写任何东西到磁盘.我的直观解决方案是传递异常,但在文档中不鼓励 - 当然有充分的理由.
class Processor(object):
...
def write(self, *args, **kwargs):
if something_bad_happens:
raise RuntimeError('This format expects %s columns: %s, got %s.' % (
(len(self.cols), self.cols, len(args))))
self.writer.writerow(args)
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
# the RuntimeError from write will be set as type, value and so on ..
# I'd like to close the stream here (release the file descriptor),
# but I do not leave a …Run Code Online (Sandbox Code Playgroud) 是否有任何可能的方法来实现sudo使用sudoers系统作为另一个用户运行封闭范围的上下文管理器?
system('whoami') # same result as echo $USER
with sudo():
system('whoami') # root
Run Code Online (Sandbox Code Playgroud)
我怀疑sudo(8)可执行文件在这里对我有帮助,但也许有一些我可以绑定的C级接口?
动机:我几乎可以将这个shell脚本完全移植到python,甚至没有任何子进程,除了我目前不得不这样做system('sudo sh -c "echo %i > /dev/thatfile"' % value).如果可以的话,它会如此优雅with sudo(), open('/dev/thatfile', 'w') as thatfile: thatfile.write(str(value)).
我已经读过,这样打开的文件会在离开with块时自动关闭:
with open("x.txt") as f:
data = f.read()
do something with data
Run Code Online (Sandbox Code Playgroud)
但是当从网上开放时,我需要这个:
from contextlib import closing
from urllib.request import urlopen
with closing(urlopen('http://www.python.org')) as page:
for line in page:
print(line)
Run Code Online (Sandbox Code Playgroud)
为什么和有什么区别?(我使用的是Python3)
我们的Python代码库具有与度量相关的代码,如下所示:
class Timer:
def __enter__(self, name):
self.name = name
self.start = time.time()
def __exit__(self):
elapsed = time.time() - self.start
log.info('%s took %f seconds' % (self.name, elapsed))
...
with Timer('foo'):
do some work
with Timer('bar') as named_timer:
do some work
named_timer.some_mutative_method()
do some more work
Run Code Online (Sandbox Code Playgroud)
在Python的术语中,计时器是一个上下文管理器.
现在我们想在C++中实现相同的东西,同样好的语法.不幸的是,C++没有with.所以"明显的"成语将是(经典的RAII)
class Timer {
Timer(std::string name) : name_(std::move(name)) {}
~Timer() { /* ... */ }
};
if (true) {
Timer t("foo");
do some work
}
if (true) {
Timer named_timer("bar");
do …Run Code Online (Sandbox Code Playgroud) 我认为这是经常出现的问题,但我一直无法找到一个好的解决方案。假设我有一个函数,可以将开放资源作为参数传递(如文件或数据库连接对象),或者需要自己创建一个函数。如果函数需要自己打开文件,最佳实践通常被认为是这样的:
with open(myfile) as fh:
# do stuff with open file handle...
Run Code Online (Sandbox Code Playgroud)
确保with退出块时文件始终关闭。但是,如果在函数中传递现有文件句柄,则该函数可能不应该关闭它本身。
考虑以下函数,它接受一个打开的文件对象或一个给出文件路径的字符串作为其参数。如果传递的是文件路径,则可能应该按上面的方式编写。否则该with语句应被省略。这会导致重复的代码:
def foo(f):
if isinstance(f, basestring):
# Path to file, need to open
with open(f) as fh:
# do stuff with fh...
else:
# Assume open file
fh = f
# do the same stuff...
Run Code Online (Sandbox Code Playgroud)
当然,可以通过定义一个辅助函数并在两个地方调用它来避免这种情况,但这看起来不太优雅。我想到的一个更好的方法是定义一个上下文管理器类来包装一个对象,如下所示:
class ContextWrapper(object):
def __init__(self, wrapped):
self.wrapped = wrapped
def __enter__(self):
return self.wrapped
def __exit__(self, *args):
pass
def foo(f):
if isinstance(f, basestring):
cm = open(f)
else: …Run Code Online (Sandbox Code Playgroud) 要以编程方式组合上下文管理器,我使用以下代码:
== helpers.py ==
from contextlib import nested
import mock
def multiple_patch(obj_to_be_patch, *methods):
return nested(
*[mock.patch.object(obj_to_be_patch, method) for method in methods]
)
Run Code Online (Sandbox Code Playgroud)
== tests.py ==
def test_foo(self):
with helpers.multiple_patch(Foo, "method1", "method2", "method3", "method3") as mocks:
mock_method1 = mocks[0]
....
# asserts on mocks
Run Code Online (Sandbox Code Playgroud)
因为我坚持使用这个版本的python我不能使用contextlib.ExitStack并且不推荐使用contextlib.nested.
谢谢
我经常需要用别的东西临时切换一个变量的值,做一些依赖于这个变量的计算,然后将变量恢复到它的原始值。例如:
var = 0
# Assign temporary value and do computation
var_ori = var
var = 1
do_something_with_var() # Function that reads the module level var variable
# Reassign original value
var = var_ori
Run Code Online (Sandbox Code Playgroud)
这似乎是使用上下文管理器(with语句)的明显机会。Python 标准库是否包含任何这样的上下文管理器?
我知道这种事情通常由其他更好的方法处理,而不是临时更改变量。然而,我并不是要求明显的解决方法。
在我的实际工作案例中,我无法更改该do_something_with_var功能。实际上,这甚至不是一个函数,而是作为元编程的一部分在全局命名空间上下文中求值的一串代码。我给出的例子是我能想到的最简单的例子,它使我的问题与临时变量有关。我没有要求获得我的示例代码的解决方法(正确版本),而是要求获得我书面问题的答案。
像 Jupyter 这样的笔记本将交互式执行分割成单元格。一般来说,这对我来说效果很好,但有一个明显的例外:上下文管理器。如果我打开一条with语句,单元格的末尾将关闭上下文,上下文管理器将退出。
假设我的笔记本处理从 DBMS 获取的数据。我通常会使用上下文管理器来管理与数据库的连接并管理游标以自动提交或回滚事务。但是在建立连接后,我的整个代码需要位于单个单元格中。
解决方法是不使用上下文管理器,上下文管理器可以帮助我们防止潜在的陷阱。在我看来,当单元可以(确实)以任何顺序执行时,这个问题在笔记本中会被放大。在我的 DBMS 示例中,由于建立连接的单元被多次执行,因此可以打开与数据库的多个连接而无需关闭。此外,它们甚至可能不是上下文管理器的“内联”版本,例如,当上下文管理器是修饰函数时@contextlib.contextmanager,尽管我没有在我使用的任何主要库中看到这种情况发生。
我可能只是在咆哮,但是有没有办法让上下文管理器单元执行友好?或者我们是否只能使用短命的上下文管理器,否则就只能依靠我们自己?
contextmanager ×10
python ×8
python-2.7 ×2
c++ ×1
c++11 ×1
exception ×1
file ×1
mocking ×1
python-3.x ×1
raii ×1
sudo ×1
timeout ×1
urlopen ×1