Как мне вернуть интересные значения из оператора with? - PullRequest
4 голосов
/ 18 мая 2009

Есть ли лучший способ, чем использовать глобальные переменные для получения интересных значений из контекстного менеджера?

@contextmanager
def transaction():
    global successCount
    global errorCount
    try:
        yield
    except:
        storage.store.rollback()
        errorCount += 1
    else:
        storage.store.commit()
        successCount += 1

Другие возможности:

  • одиночки

    вроде глобалов ...

  • кортеж в качестве аргумента для диспетчера контекста

    делает функцию более специфичной для проблемы / менее пригодной для повторного использования

  • экземпляр, который содержит определенные атрибуты в качестве аргумента диспетчера контекста

    те же проблемы, что и у кортежа, но более разборчиво

  • вызвать исключение в конце диспетчера контекста, содержащего значения.

    действительно плохая идея

Ответы [ 3 ]

7 голосов
/ 18 мая 2009

См. http://docs.python.org/reference/datamodel.html#context-managers

Создайте класс, в котором хранится число успешных и ошибочных операций и который реализует методы __enter__ и __exit__.

3 голосов
/ 18 мая 2009

Я все еще думаю, что вы должны создать класс для хранения количества ошибок / успехов, как я сказал в вас последний вопрос . Я предполагаю, что у вас есть свой собственный класс, так что просто добавьте что-то вроде этого:

class transaction:
    def __init__(self):
        self.errorCount = 0
        self.successCount = 0  

    def __enter__(*args):
        pass  

    def __exit__(self, type, value, traceback):
        if type:
            storage.store.rollback()
            self.errorCount += 1
        else:
            storage.store.commit()
            self.successCount += 1

(type - Нет, если нет исключений после вызова contextmanager)

И тогда вы, вероятно, уже где-то используете это, что вызовет contextmanager и запустит ваш код __exit__(). Редактировать: Как прокомментировал Eli, создавайте новый экземпляр транзакции только тогда, когда вы хотите сбросить coutners.

t = transaction()
for q in queries:
    with t:
        t.execute(q)
0 голосов
/ 18 мая 2009

"кортеж в качестве аргумента для диспетчера контекста

делает функцию более специфичной для проблемы / менее пригодной для повторного использования "

Ложные.

Это заставляет менеджер контекста сохранять состояние.

Если вы не реализуете ничего, кроме этого, оно будет использоваться повторно.

Однако вы не можете использовать кортеж, потому что он неизменный. Вам нужна изменчивая коллекция. В голову приходят словари и определения классов.

Следовательно, рекомендуемая реализация

"экземпляр, который содержит определенные атрибуты в качестве аргумента диспетчера контекста"

Простое определение класса с двумя атрибутами - это все, что вам нужно. Тем не менее, ваш статус транзакции является состоянием, и вам нужно где-то сохранить состояние.

class Counters(dict):
    SUCCEED= 0
    FAIL= 1
    def __init__( self ):
        self[ self.SUCCEED ]= 0
        self[ self.FAIL ]= 0 
    def increment( self, status ):
        self[status] += 1

class Transaction(object):
    def __init__( self, worker, counters ):
        self.worker= worker
        self.counters= counters
    def __enter__( self ):
        self.counters.status= None
    def process( self, *args, **kw ):
        status= self.worker.execute( *args, **kw )
        self.counters.increment( status )
    def __exit__( self ):
        pass

counts= Counters()
for q in queryList:
    with Transaction(execQuery,counts) as t:
        t.process( q )
print counts
...