在with语句中如何返回有趣的值?

10 投票
3 回答
12432 浏览
提问于 2025-04-15 11:40

有没有比使用全局变量更好的方法来从上下文管理器中获取有趣的值呢?

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

其他可能的选择:

  • 单例模式

    有点像全局变量……

  • 将元组作为上下文管理器的参数

    这样做让函数更专注于特定问题,但可重用性降低了。

  • 将持有特定属性的实例作为上下文管理器的参数

    和元组一样的问题,不过更容易理解。

  • 在上下文管理器结束时抛出一个异常,包含这些值。

    这个主意真的很糟糕。

3 个回答

0

“将元组作为上下文管理器的参数”

“让这个函数更专注于某个问题,变得不那么通用”

这是错误的。

这样做会让上下文管理器保持状态。

如果你只实现这些,它还是可以重复使用的。

不过,你实际上不能使用元组,因为元组是不可变的。你需要一些可变的集合,比如字典或者类的定义。

因此,推荐的实现方式是

“实例化一个类,将特定的属性作为上下文管理器的参数”

你只需要一个简单的类定义,里面有两个属性。但是,你的事务状态是有状态的,需要在某个地方保持这个状态。

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
5

我还是觉得你应该创建一个类来保存你的错误和成功的计数,就像我在你上一个问题中说的那样。我猜你已经有自己的类了,所以只需要在里面加上类似这样的代码:

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 如果没有异常发生,则为 None,当你调用 contextmanager 时)

然后你可能已经在某个地方使用了这个,这样就会调用 contextmanager 并运行你的 __exit__() 代码。 补充:正如 Eli 评论的那样,只有在你想重置计数器时才创建一个新的事务实例。

t = transaction()
for q in queries:
    with t:
        t.execute(q)
9

查看 http://docs.python.org/reference/datamodel.html#context-managers

创建一个类,用来记录成功和错误的次数,并且实现 __enter____exit__ 这两个方法。

撰写回答