代码之家  ›  专栏  ›  技术社区  ›  Cloud

在Python中调用上下文管理器的多种方法

  •  6
  • Cloud  · 技术社区  · 7 年前

    背景

    我在Python中有一个类,它接受互斥体列表。然后对该列表进行排序,并使用 __enter__() __exit__() 以特定顺序锁定/解锁所有互斥锁,以防止死锁。

    类目前为我们节省了许多潜在死锁的麻烦,因为我们可以在 RAII style ,即:

    self.lock = SuperLock(list_of_locks)
    # Lock all mutexes.
    with self.lock:
        # Issue calls to all hardware protected by these locks.
    

    问题

    我们希望公开这个类提供一个raii风格的api的方法,这样当以某种方式调用时,我们就可以一次锁定一半的互斥体,即:

    self.lock = SuperLock(list_of_locks)
    # Lock all mutexes.
    with self.lock:
        # Issue calls to all hardware protected by these locks.
    
    # Lock the first half of the mutexes in SuperLock.list_of_locks
    with self.lock.first_half_only:
        # Issue calls to all hardware protected by these locks.
    
    # Lock the second half of the mutexes in SuperLock.list_of_locks
    with self.lock.second_half_only:
        # Issue calls to all hardware protected by these locks.
    

    问题

    是否有方法提供这种类型的功能以便我可以调用 with self.lock.first_half_only with self.lock_first_half_only () 为用户提供一个简单的API?我们希望将所有这些功能保存在一个类中。

    谢谢您。

    3 回复  |  直到 7 年前
        1
  •  2
  •   wim    7 年前

    是的,您可以得到这个接口。将在WITH语句上下文中输入/退出的对象是已解析的属性。因此,您可以继续将上下文管理器定义为上下文管理器的属性:

    from contextlib import ExitStack  # pip install contextlib2
    from contextlib import contextmanager
    
    @contextmanager
    def lock(name):
        print("entering lock {}".format(name))
        yield
        print("exiting lock {}".format(name))
    
    @contextmanager
    def many(contexts):
        with ExitStack() as stack:
            for cm in contexts:
                stack.enter_context(cm)
            yield
    
    class SuperLock(object):
    
        def __init__(self, list_of_locks):
            self.list_of_locks = list_of_locks
    
        def __enter__(self):
            # implement for entering the `with self.lock:` use case
            return self
    
        def __exit__(self, exce_type, exc_value, traceback):
            pass
    
        @property
        def first_half_only(self):
            return many(self.list_of_locks[:4])
    
        @property
        def second_half_only(self):
            # yo dawg, we herd you like with-statements
            return many(self.list_of_locks[4:])
    

    当您创建并返回一个新的上下文管理器时,您可以使用实例中的状态(即 self )

    示例用法:

    >>> list_of_locks = [lock(i) for i in range(8)] 
    >>> super_lock = SuperLock(list_of_locks) 
    >>> with super_lock.first_half_only: 
    ...     print('indented') 
    ...   
    entering lock 0
    entering lock 1
    entering lock 2
    entering lock 3
    indented
    exiting lock 3
    exiting lock 2
    exiting lock 1
    exiting lock 0
    

    编辑 :基于类的等价于 lock 上面显示的生成器上下文管理器

    class lock(object):
    
        def __init__(self, name):
            self.name = name
    
        def __enter__(self):
            print("entering lock {}".format(self.name))
            return self
    
        def __exit__(self, exce_type, exc_value, traceback):
            print("exiting lock {}".format(self.name))
            # If you want to handle the exception (if any), you may use the
            # return value of this method to suppress re-raising error on exit
    
        2
  •  1
  •   Sraw    7 年前
    from contextlib import contextmanager
    
    class A:
    
        @contextmanager
        def i_am_lock(self):
            print("entering")
            yield
            print("leaving")
    
    a = A()
    
    with a.i_am_lock():
        print("inside")
    

    输出:

    entering
    inside
    leaving
    

    您还可以使用 contextlib.ExitStack 更好地管理你的锁。

        3
  •  1
  •   Felix    7 年前

    我会用一个 SimpleNamespace 允许属性访问不同的 SuperLock 对象,例如:

    from types import SimpleNamespace
    
    self.lock = SimpleNamespace(
        all=SuperLock(list_of_locks),
        first_two_locks=SuperLock(list_of_locks[:2]),
        other_locks=SuperLock(list_of_locks[2:])
    )
    
    with self.lock.all:
        # Issue calls to all hardware protected by these locks.
    
    with self.lock.first_two_locks:
        # Issue calls to all hardware protected by these locks.
    
    with self.lock.other_locks:
        # Issue calls to all hardware protected by these locks.
    

    编辑:

    对于Python2,可以使用这个类来实现类似的行为:

    class SimpleNamespace:
        def __init__(self, **kwargs):
            self.__dict__.update(kwargs)