【问题标题】:Possible to create a @synchronized decorator that's aware of a method's object?可以创建一个知道方法对象的@synchronized 装饰器吗?
【发布时间】:2015-06-06 19:08:18
【问题描述】:

我正在尝试创建一个 @synchronized 包装器,该包装器为每个对象创建一个 Lock 并使方法调用线程安全。如果我可以访问包装方法中的方法的method.im_self,我只能这样做。

    class B:
        def f(self): pass

    assert inspect.ismethod( B.f ) # OK
    assert inspect.ismethod( B().f ) # OK
    print B.f    # <unbound method B.f>
    print B().f  # <bound method B.f of <__main__.B instance at 0x7fa2055e67e8>>



    def synchronized(func):
        # func is not bound or unbound!
        print func  # <function f at 0x7fa20561b9b0>    !!!!

        assert inspect.ismethod(func)  # FAIL
        # ... allocate one lock per C instance
        return func

    class C:
        @synchronized
        def f(self): pass

(1) 令人困惑的是,传递给我的装饰器的 func 参数在传递给包装器生成器之前会更改类型。这似乎是粗鲁和不必要的。为什么会这样?

(2) 是否有一些装饰器魔术,我可以通过它对对象互斥锁进行方法调用(即每个对象一个锁,而不是每个类)。

更新:@synchronized(lock) 包装器有很多示例。然而,我真正想要的是@synchronized(self)。我可以这样解决:

    def synchronizedMethod(func):
        def _synchronized(*args, **kw):
             self = args[0]
             lock = oneLockPerObject(self)
             with lock: return func(*args, **kw)
        return _synchronized

但是,因为它更有效,我更喜欢:

    def synchronizedMethod(func):
        lock = oneLockPerObject(func.im_self)

        def _synchronized(*args, **kw):
             with lock: return func(*args, **kw)

        return _synchronized

这可能吗?

【问题讨论】:

    标签: python decorator python-decorators


    【解决方案1】:

    你不能在装饰时获得self,因为装饰器是在函数定义时应用的。尚无self;事实上,还不存在。

    如果您愿意将您的锁存储在实例上(可以说是每个实例的值应该去的地方),那么这可能会做到:

    def synchronized_method(func):
        def _synchronized(self, *args, **kw):
             if not hasattr(self, "_lock"): self._lock = oneLockPerObject(self)
             with self._lock: return func(self, *args, **kw)
        return _synchronized
    

    您还可以在某种基类上的__init__() 方法中生成锁,并以相同的方式将其存储在实例中。这简化了您的装饰器,因为您不必检查 self._lock 属性是否存在。

    【讨论】:

    • 这是更好的答案,IMO。与通常假定全局锁的装饰器相比,它更安全,适合更多用例。
    • 小错字,但应该是hasattr
    【解决方案2】:

    去阅读:

    特别是:

    wrapt 模块然后包含那里描述的@synchronized 装饰器。

    完整的实现足够灵活:

    @synchronized # lock bound to function1
    def function1():
        pass 
    
    @synchronized # lock bound to function2
    def function2():
        pass 
    
    @synchronized # lock bound to Class
    class Class(object):  
    
        @synchronized # lock bound to instance of Class
        def function_im(self):
            pass 
    
        @synchronized # lock bound to Class
        @classmethod
        def function_cm(cls):
            pass
    
        @synchronized # lock bound to function_sm
        @staticmethod
        def function_sm():
            pass
    

    还有上下文管理器之类的用法:

    class Object(object):  
    
        @synchronized
        def function_im_1(self):
            pass  
    
        def function_im_2(self):
            with synchronized(self):
                pass
    

    更多信息和示例也可以在以下位置找到:

    您还可以在以下位置观看有关如何实施的会议演讲:

    【讨论】:

    • 记得使用来自wrapt模块的decorator
    【解决方案3】:

    (1) 令人困惑的是func参数传递给我的装饰器 在将类型传递到包装器生成器之前更改类型。这 似乎是粗鲁和不必要的。为什么会这样?

    没有!相反,函数对象(和其他描述符)在调用它们的方法时会产生它们的__get__ 的结果——而那个结果就是方法对象!

    但存在于class__dict__ 中的始终是描述符——特别是函数 对象!看看吧……:

    >>> class X(object):
    ...   def x(self): pass
    ... 
    >>> X.__dict__['x']
    <function x at 0x10fe04e60>
    >>> type(X.__dict__['x'])
    <type 'function'>
    

    看到了吗?到处都没有方法对象根本!-)

    因此,在装修时也没有im_self——你需要采用基于内省的替代想法。

    【讨论】:

    • 谢谢——有帮助。为什么语言会从函数中生成未绑定的方法?为什么不总是有一个未绑定的方法?这似乎也阻止了每个类的同步。
    • @user48956,在 Python 3(从 3.0 开始)只有 bound 方法 -- unbound 方法已从语言中删除,所以只有 两个 类型,函数和绑定方法,可以更简单地完成这一切。也就是说,在 Python 2.0(15 年前)的远古时代,设计比必要的要复杂得多。但是,3.0 中的改进远比您建议的要简化得多,并且至少不会帮助您追求您喜欢的方法(即使您的装饰器的 arg 未绑定方法,它的im_self 将是None,所以,无论如何都没有帮助!-)。
    猜你喜欢
    • 2020-02-10
    • 2012-07-29
    • 2011-12-30
    • 2019-07-22
    • 2011-05-18
    • 2014-02-09
    • 1970-01-01
    • 2011-04-11
    相关资源
    最近更新 更多