Python与......以及自定义上下文管理器

Bri*_*and 29 python

我在Python中编写了一个简单的上下文管理器来处理单元测试(并尝试学习上下文管理器):

class TestContext(object):
    test_count=1
    def __init__(self):
        self.test_number = TestContext.test_count
        TestContext.test_count += 1

    def __enter__(self):
        pass

    def __exit__(self, exc_type, exc_value, exc_traceback):
        if exc_value == None:
            print 'Test %d passed' %self.test_number
        else:
            print 'Test %d failed: %s' %(self.test_number, exc_value)
        return True
Run Code Online (Sandbox Code Playgroud)

如果我按如下方式编写测试,一切正常.

test = TestContext()
with test:
   print 'running test %d....' %test.test_number
   raise Exception('this test failed')
Run Code Online (Sandbox Code Playgroud)

但是,如果我尝试使用... as,我不会获得对TestContext()对象的引用.运行这个:

with TestContext() as t:
    print t.test_number
Run Code Online (Sandbox Code Playgroud)

引发异常'NoneType' object has no attribute 'test_number'.

我哪里错了?

Mor*_*app 28

假设您需要访问with语句中创建的上下文管理器,__enter__需要返回self.如果您不需要访问它,__enter__可以返回您想要的任何内容.

with语句将此方法的返回值绑定到语句的as子句中指定的目标(如果有).

这会奏效.

class TestContext(object):
    test_count=1
    def __init__(self):
        self.test_number = TestContext.test_count
        TestContext.test_count += 1

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, exc_traceback):
        if exc_value == None:
            print 'Test %d passed' % self.test_number
        else:
            print 'Test %d failed: %s' % (self.test_number, exc_value)
        return True
Run Code Online (Sandbox Code Playgroud)

  • 请注意,上下文管理器没有*要求*返回“self”。这是一个非常方便的默认值,因此您可以在“with”语句中创建上下文管理器,但上下文管理器可以自由地从“__enter__”返回任何它喜欢的内容。对于OP的特定用例,在“with”语句中创建“TestContext()”,返回“self”确实是正确的方法。 (2认同)

glg*_*lgl 9

def __enter__(self):
    return self
Run Code Online (Sandbox Code Playgroud)

会使它工作.从此方法返回的值将分配给as变量.

另请参阅Python文档:

如果目标包含在with语句中,__enter__()则会为其分配返回值.

如果您只需要数字,您甚至可以将上下文管理器的逻辑更改为

class TestContext(object):
    test_count=1
    def __init__(self):
        self.test_number = TestContext.test_count
        TestContext.test_count += 1

    def __enter__(self):
        return self.test_number

    def __exit__(self, exc_type, exc_value, exc_traceback):
        if exc_value == None:
            print 'Test %d passed' % self.test_number
        else:
            print 'Test %d failed: %s' % (self.test_number, exc_value)
        return True
Run Code Online (Sandbox Code Playgroud)

然后呢

with TestContext() as test_number:
    print test_number
Run Code Online (Sandbox Code Playgroud)


art*_*pov 6

根据PEP 343,with EXPR as VAR语句不会分配给VAR的结果EXPR,而是分配给的结果EXPR.__enter__()。第一个示例有效,因为您引用了test变量本身。