可以创建一个知道方法对象的@synchronized 装饰器吗?
Possible to create a @synchronized decorator that's aware of a method's object?
我正在尝试创建一个@synchronized 包装器,它为每个对象创建一个锁并使方法调用线程安全。如果我可以访问包装方法中的方法 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) 是否有一些装饰魔法,我可以通过它对对象进行方法调用互斥锁(即每个对象一个锁,而不是每个 class)。
更新:有很多@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
这可能吗?
(1) What's confusing is that the func parameter passed to my decorator
changes type before it gets passed into the wrapper-generator. This
seem is rude and unnecessary. Why does this happen?
没有!相反,函数对象(和其他描述符)在调用它们的那个方法时产生它们的 __get__
结果——并且 那个 结果就是方法对象!
但是 class
的 __dict__
中始终存在描述符——具体来说,就是 函数 对象!看看...:[=16=]
>>> class X(object):
... def x(self): pass
...
>>> X.__dict__['x']
<function x at 0x10fe04e60>
>>> type(X.__dict__['x'])
<type 'function'>
看到了吗?任何地方都没有方法对象 !-)
因此,在装饰时也没有 im_self
-- 你需要采用基于内省的替代想法。
你无法在装饰时获得 self
,因为装饰器是在函数定义时应用的。 self
还不存在;事实上,class 还不存在。
如果您愿意将锁 存储在实例 上(这可以说是每个实例值应该去的地方)那么这可能会做:
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__()
方法中基于某种基础 class 生成锁,并以相同的方式将其存储在实例上。这简化了装饰器,因为您不必检查 self._lock
属性是否存在。
去阅读:
特别是:
- https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/07-the-missing-synchronized-decorator.md
- https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/08-the-synchronized-decorator-as-context-manager.md
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
还可以在以下位置找到更多信息和示例:
还有一个会议演讲,您可以在以下位置观看它是如何实现的:
我正在尝试创建一个@synchronized 包装器,它为每个对象创建一个锁并使方法调用线程安全。如果我可以访问包装方法中的方法 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) 是否有一些装饰魔法,我可以通过它对对象进行方法调用互斥锁(即每个对象一个锁,而不是每个 class)。
更新:有很多@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
这可能吗?
(1) What's confusing is that the func parameter passed to my decorator changes type before it gets passed into the wrapper-generator. This seem is rude and unnecessary. Why does this happen?
没有!相反,函数对象(和其他描述符)在调用它们的那个方法时产生它们的 __get__
结果——并且 那个 结果就是方法对象!
但是 class
的 __dict__
中始终存在描述符——具体来说,就是 函数 对象!看看...:[=16=]
>>> class X(object):
... def x(self): pass
...
>>> X.__dict__['x']
<function x at 0x10fe04e60>
>>> type(X.__dict__['x'])
<type 'function'>
看到了吗?任何地方都没有方法对象 !-)
因此,在装饰时也没有 im_self
-- 你需要采用基于内省的替代想法。
你无法在装饰时获得 self
,因为装饰器是在函数定义时应用的。 self
还不存在;事实上,class 还不存在。
如果您愿意将锁 存储在实例 上(这可以说是每个实例值应该去的地方)那么这可能会做:
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__()
方法中基于某种基础 class 生成锁,并以相同的方式将其存储在实例上。这简化了装饰器,因为您不必检查 self._lock
属性是否存在。
去阅读:
特别是:
- https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/07-the-missing-synchronized-decorator.md
- https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/08-the-synchronized-decorator-as-context-manager.md
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
还可以在以下位置找到更多信息和示例:
还有一个会议演讲,您可以在以下位置观看它是如何实现的: