Подтвердить что ты не робот

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

Если я напишу тест следующим образом, все будет хорошо.

test = TestContext()
with test:
   print 'running test %d....' %test.test_number
   raise Exception('this test failed')

Однако, если я пытаюсь использовать с... как, я не получаю ссылку на объект TestContext(). Выполнение этого:

with TestContext() as t:
    print t.test_number

Вызывает исключение 'NoneType' object has no attribute 'test_number'.

Где я иду не так?

4b9b3361

Ответ 1

Предполагая, что вам нужно получить доступ к менеджеру контекста, созданному в инструкции 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

Ответ 2

def __enter__(self):
    return self

заставит его работать. Значение, возвращаемое этим методом, будет присвоено переменной 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

а затем do

with TestContext() as test_number:
    print test_number

Ответ 3

Согласно PEP 343, оператор with EXPR as VAR не присваивает VAR результат EXPR, а результат EXPR.__enter__(). Первый пример работал, потому что вы ссылались на переменную test.