我应该如何从with语句中返回有趣的值?

时间:2021-03-02 05:29:24

Is there a better way than using globals to get interesting values from a context manager?

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

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

Other possibilities:

  • singletons

    sort of globals...

    有点全局...

  • tuple as an argument to the context manager

    makes the function more specific to a problem /less reusable

    使功能更加特定于问题/更少可重用

  • instance that holds the specific attributes as an argument to the context manager

    same problems as the tuple, but more legible

    与元组相同的问题,但更清晰

  • raise an exception at the end of the context manager holding the values.

    really bad idea

    真的很糟糕

3 个解决方案

#1


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

Create a class which holds the success and error counts, and which implements the __enter__ and __exit__ methods.

创建一个包含成功和错误计数的类,并实现__enter__和__exit__方法。

#2


I still think you should be creating a class to hold you error/success counts, as I said in you last question. I'm guessing you have your own class, so just add something like this to it:

我仍然认为你应该创建一个课程来控制错误/成功,正如我在上一个问题中所说的那样。我猜你有自己的类,所以只需添加这样的东西:

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 is None if there are no exceptions once invoking the contextmanager)

(如果在调用contextmanager后没有异常,则type为None)

And then you probably are already using this somewhere, which will invoke the contextmanager and run your __exit__() code. Edit: As Eli commented, only create a new transaction instance when you want to reset the coutners.

然后你可能已经在某处使用了它,它将调用contextmanager并运行你的__exit __()代码。编辑:正如Eli评论的那样,只有在您想要重置coutner时才创建新的事务实例。

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

#3


"tuple as an argument to the context manager

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

makes the function more specific to a problem /less reusable"

使功能更加特定于问题/更少可重复使用“

False.

This makes the context manager retain state.

这使得上下文管理器保持状态。

If you don't implement anything more than this, it will be reusable.

如果您没有实现任何更多,它将是可重用的。

However, you can't actually use a tuple because it's immutable. You need some mutable collection. Dictionaries and class definitions come to mind.

但是,您实际上不能使用元组,因为它是不可变的。你需要一些可变的收藏。我想到了字典和类定义。

Consequently, the recommended implementation is

因此,建议的实施是

"instance that holds the specific attributes as an argument to the context manager"

“将特定属性作为参数提供给上下文管理器的实例”

A simple class definition with two attributes is all you need. However, your transaction status is stateful and you need to retain state somewhere.

您只需要一个具有两个属性的简单类定义。但是,您的事务状态是有状态的,您需要在某处保留状态。

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

#1


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

Create a class which holds the success and error counts, and which implements the __enter__ and __exit__ methods.

创建一个包含成功和错误计数的类,并实现__enter__和__exit__方法。

#2


I still think you should be creating a class to hold you error/success counts, as I said in you last question. I'm guessing you have your own class, so just add something like this to it:

我仍然认为你应该创建一个课程来控制错误/成功,正如我在上一个问题中所说的那样。我猜你有自己的类,所以只需添加这样的东西:

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 is None if there are no exceptions once invoking the contextmanager)

(如果在调用contextmanager后没有异常,则type为None)

And then you probably are already using this somewhere, which will invoke the contextmanager and run your __exit__() code. Edit: As Eli commented, only create a new transaction instance when you want to reset the coutners.

然后你可能已经在某处使用了它,它将调用contextmanager并运行你的__exit __()代码。编辑:正如Eli评论的那样,只有在您想要重置coutner时才创建新的事务实例。

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

#3


"tuple as an argument to the context manager

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

makes the function more specific to a problem /less reusable"

使功能更加特定于问题/更少可重复使用“

False.

This makes the context manager retain state.

这使得上下文管理器保持状态。

If you don't implement anything more than this, it will be reusable.

如果您没有实现任何更多,它将是可重用的。

However, you can't actually use a tuple because it's immutable. You need some mutable collection. Dictionaries and class definitions come to mind.

但是,您实际上不能使用元组,因为它是不可变的。你需要一些可变的收藏。我想到了字典和类定义。

Consequently, the recommended implementation is

因此,建议的实施是

"instance that holds the specific attributes as an argument to the context manager"

“将特定属性作为参数提供给上下文管理器的实例”

A simple class definition with two attributes is all you need. However, your transaction status is stateful and you need to retain state somewhere.

您只需要一个具有两个属性的简单类定义。但是,您的事务状态是有状态的,您需要在某处保留状态。

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