Python 类实例中的上下文管理器

Python 类实例中的上下文管理器,python,Python,我想创建一个特定于另一个类实例的上下文管理器类。我可以通过调用创建类的方法来实现这一点,但我不确定这是最好、最好的方法: class MyClass(object): def __init__(self): self.level = 0 self.Nest = self.create_context_manager() def inclev(self): self.level += 1 def declev(self):

我想创建一个特定于另一个类实例的上下文管理器类。我可以通过调用创建类的方法来实现这一点,但我不确定这是最好、最好的方法:

class MyClass(object):
    def __init__(self):
        self.level = 0
        self.Nest = self.create_context_manager()
    def inclev(self):
        self.level += 1
    def declev(self):
        self.level -= 1

    def create_context_manager(self):
        self2 = self
        class Nest(object):
            def __init__(self):
                pass
            def __enter__(self):
                self2.inclev()
            def __exit__(self, exc_type, exc_value, traceback):
                self2.declev()
        return Nest

# Manually increase/decrease level
my_instance = MyClass()
print(my_instance.level)
my_instance.inclev()
print(my_instance.level)
my_instance.inclev()
print(my_instance.level)
my_instance.declev()
print(my_instance.level)
my_instance.declev()
print(my_instance.level)

# Use instance-specific context manager
other_instance = MyClass()
print(other_instance.level)
with other_instance.Nest():
    print(other_instance.level)
    with other_instance.Nest():
        print(other_instance.level)
    print(other_instance.level)
print(other_instance.level)

为什么需要嵌套类?只需让主要对象直接实现上下文管理协议:

class MyClass(object):
    def __init__(self):
        self.level = 0
    def inclev(self):
        self.level += 1
    def declev(self):
        self.level -= 1

    __enter__ = inclev  # For __enter__, just alias inclev, no need for wrapper
    def __exit__(self, exc_type, exc_value, traceback):
        self.declev()
然后只需将其用于:

with other_instance:
    print(other_instance.level)
    with other_instance:
        print(other_instance.level)
    print(other_instance.level)
如果您确实需要将上下文管理器协议作为一个名为
Nest
的构造对象,您仍然可以使用
contextlib
中的内置内容简化一点:

from contextlib import contextmanager

class MyClass(object):
    def __init__(self):
        self.level = 0
    def inclev(self):
        self.level += 1
    def declev(self):
        self.level -= 1

    @contextmanager
    def Nest(self):
        self.inclev()
        try:
            yield
        finally:
            self.declev()

如果您使用标准命名约定,它确实可以帮助人们阅读您的代码。它还可以使语法突出显示工作。看看pep8,我已经编辑了你的代码(非功能性的)。你能检查一下我没有改变任何与这个问题相关的事情吗?@Holloway对此表示抱歉。是的,更改是可以的。我可能需要在类中使用不同的上下文管理器。我仍然可以用一种方式(通过传递参数)实现多个名称,但我更希望不同名称的简单性(从使用角度来看)。无论如何,我会考虑这个问题。杰勒比:我刚刚完成了“与问题相同的用法模式,但简化代码”的例子(我先发布了“最好”的方法,然后编辑添加另一个方法)。这将涵盖您想要的用例。谢谢,我更喜欢第二种方式。虽然可能有一种情况会让这看起来很自然,但现在它很可怕。为了不惹恼一年后必须阅读此代码的人(特别是如果是你自己!),让对象本身实现嵌套行为而没有任何指示器是令人毛骨悚然的。@AustinHastings:也许吧。取决于对象是否主要与标高跟踪有关
threading.RLock
基本上是相同的设计,它在不是所有者时锁定,并递增/递减计数,直到达到0,然后解锁,您只需将其与myrlock:一起使用即可。在一个类中混合许多不同的行为是令人困惑的;这实际上与使用(或不使用)上下文管理器无关。