如果建造和破坏是昂贵的,如何避免再次建造背景?

2024-04-20 08:34:52 发布

您现在位置:Python中文网/ 问答频道 /正文

如果构建和销毁上下文很重,我如何检测到我已经有了上下文,并且不需要再次构建它?换句话说,我希望嵌套上下文2什么也不做。你知道吗

我可能需要在一个循环中多次调用上下文,是否可以将整个循环放在with子句中,然后不知何故,循环中的上下文知道它们已经在一个上下文中,然后跳过构建上下文,就好像内部with子句没有效果一样?(在下面的示例中,我想完全跳过上下文2)

from contextlib import contextmanager

@contextmanager
def myContext(i):

    print 'build context', i

    try:
        yield
    finally:
        print 'exiting context',i

def func():
    print 'call func'

with myContext(1):

    print 'outside'

    with myContext(2):
        func()

代码输出:

build context 1
outside
build context 2
call func
exiting context 2
exiting context 1

Tags: frombuild示例defwithcontextcallfunc
3条回答

我使用元类将@Blckknght的答案与Creating a singleton in Python中的Method 3结合起来。你知道吗

from abc import ABCMeta, abstractmethod
from functools import wraps
import time

class Singleton(ABCMeta):
    _instance = None

    def __call__(cls, *args, **kwargs):
        if not hasattr(cls, "_instance") or cls._instance is None:
            cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instance


class ContextDecorator(object):

    __metaclass__ = Singleton

    def __init__(self, *args, **kwargs):
        self.args = args
        self.__dict__.update(kwargs)
        self._built = False
        self._contextExists = False

    @abstractmethod
    def _build(self):
        pass

    @abstractmethod
    def _destory(self):
        pass

    @classmethod
    def clear_singleton(cls):
        cls._instance = None

    def __enter__(self):
        if not self._built:
            self._build()
            self._built = True
            print 'call _build first time'
        else:
            print 'skip _build'
            self._contextExists = True

        return self

    def __exit__(self, typ, val, traceback):
        if not self._contextExists:
            self._destory()
            self.clear_singleton()
            # self._contextExists=False
            print 'call _destory first time'
        else:
            print 'skip _destory'
            self._contextExists = False

    def __call__(self, f):
        self.function = f

        @wraps(f)
        def wrapper(*args, **kw):
            with self:
                try:
                    return f(*args, **kw)
                except:
                    raise
        return wrapper


class CustomContext(ContextDecorator):

    def __init__(self, *args, **kwargs):
        super(CustomContext, self).__init__(*args, **kwargs)

    def _build(self):
        pass

    def _destory(self):
        pass




print 'context managere test'

with CustomContext():
    for i in range(3):
        with CustomContext():
            time.sleep(0.01)


print '-' * 10
print 'decorator test'


@CustomContext()
@CustomContext()
def test():
    print 'in side test func'


test()

以及输出

context managere test
call _build first time
skip _build
skip _destory
skip _build
skip _destory
skip _build
skip _destory
call _destory first time
     
decorator test
call _build first time
skip _build
in side test func
skip _destory
call _destory first time

您可以创建全局引用计数:

_my_context_call_count = 0

@contextmanager
def my_context(i):
    global _my_context_call_count

    if _my_context_call_count == 0:
        print 'build context', i
    _my_context_call_count += 1

    try:
        yield

    finally:
        _my_context_call_count -= 1
        if _my_context_call_count == 0:
            print 'exiting context', i

(请注意,这不是线程安全的,但程序是单线程的并不重要。)

使用作为单例的上下文管理器类,而不是在函数上使用contextmanager修饰符,可能是有意义的。这与kennytm使用全局变量的答案没有本质区别,但是我的版本将全局状态隐藏在类变量中,而不是常规的全局变量中。你知道吗

class MyContext(object):
    _instance = None

    def __new__(cls, i):
        if cls._instance is None:
            cls._instance = super(cls, MyContext).__new__(cls, i)
            cls._instance.depth = 0
        return cls._instance

    def __init__(self, i):
        self.i = i

    def __enter__(self):
        self.depth += 1
        if self.depth == 1:
            self.some_expensive_value = expensive_calculation(self.i)
        return self       # or maybe return self.some_expensive_value?

    def __exit__(self, exec_type, exec_value, traceback):
        self.depth -= 1
        if self.depth == 0:
            self.expensive_value.close()   # clean up value if necessary

这种方法不是线程安全的,尽管您可以通过添加一个锁来实现,该锁是围绕self.depth的更改和检查而设置的。昂贵的计算只针对对上下文管理器的任何嵌套调用数的最外层运行。你知道吗

相关问题 更多 >