装饰方法时访问绑定方法或自身
Accessing bound method or self when decorating a method
我有一个用例,我想用一种额外的调用方式来修饰一个方法,例如在这段代码中:
def decorator(func):
def enhanced(*args, **kwargs):
func(*args, **kwargs)
func.enhanced = enhanced
return func
@decorator
def function():
pass
class X:
@decorator
def function(self):
pass
x = X()
function()
function.enhanced()
x.function()
# x.function.enhanced()
x.function.enhanced(x)
前三个调用按预期工作,但 x.function.enhanced()
没有;我必须写 x.function.enhanced(x)
才能让它工作。我知道这是因为传递给装饰器的 func
不是绑定方法而是函数,因此需要显式传递 self
。
但是我该如何解决这个问题呢?从我对描述符的一点点了解来看,它们仅在查找 class 时相关,并且由于 func
不是 class,因此未查找 func.enhanced
以一种我可以拦截的方式。
这里有什么我可以做的吗?
您可以 return 一个 descriptor return 是一个对象,它使自己可调用并且具有映射到您的 enhanced
包装函数的 enhanced
属性:
from functools import partial
def decorator(func):
class EnhancedProperty:
# this allows function.enhanced() to work
def enhanced(self, *args, **kwargs):
print('enhanced', end=' ') # this output is for the demo below only
return func(*args, **kwargs)
# this allows function() to work
def __call__(self, *args, **kwargs):
return func(*args, **kwargs)
def __get__(self, obj, objtype):
class Enhanced:
# this allows x.function() to work
__call__ = partial(func, obj)
# this allows x.function.enhanced() to work
enhanced = partial(self.enhanced, obj)
return Enhanced()
return EnhancedProperty()
这样:
@decorator
def function():
print('function')
class X:
@decorator
def function(self):
print('method of %s' % self.__class__.__name__)
x = X()
function()
function.enhanced()
x.function()
x.function.enhanced()
会输出:
function
enhanced function
method of X
enhanced method of X
就像我在对@blhsing 发布的答案的评论中的意思的例子:
class EnhancedProperty:
def __init__(self, func):
self.func = func
def enhanced(self, *args, **kwargs):
return self.func(*args, **kwargs)
def __call__(self, *args, **kwargs):
return self.func(*args, **kwargs)
def __get__(self, obj, typ):
return Enhanced(self.func, obj, typ)
class Enhanced:
def __init__(self, func, obj, typ):
self.func = func
self.obj = obj
self.typ = typ
def __call__(self, *args, **kwargs):
return self.func.__get__(self.obj, self.typ)(*args, **kwargs)
def enhanced(self, *args, **kwargs):
return self.func(self.obj, *args, **kwargs)
def decorator(f):
return EnhancedProperty(f)
在 REPL 中:
In [2]: foo(8, -8)
Out[2]: 1040
In [3]: foo.enhanced(8, -8)
Out[3]: 1040
In [4]: Bar().baz('foo')
Out[4]: ('foo', 'foo')
In [5]: Bar().baz.enhanced('foo')
Out[5]: ('foo', 'foo')
我有一个用例,我想用一种额外的调用方式来修饰一个方法,例如在这段代码中:
def decorator(func):
def enhanced(*args, **kwargs):
func(*args, **kwargs)
func.enhanced = enhanced
return func
@decorator
def function():
pass
class X:
@decorator
def function(self):
pass
x = X()
function()
function.enhanced()
x.function()
# x.function.enhanced()
x.function.enhanced(x)
前三个调用按预期工作,但 x.function.enhanced()
没有;我必须写 x.function.enhanced(x)
才能让它工作。我知道这是因为传递给装饰器的 func
不是绑定方法而是函数,因此需要显式传递 self
。
但是我该如何解决这个问题呢?从我对描述符的一点点了解来看,它们仅在查找 class 时相关,并且由于 func
不是 class,因此未查找 func.enhanced
以一种我可以拦截的方式。
这里有什么我可以做的吗?
您可以 return 一个 descriptor return 是一个对象,它使自己可调用并且具有映射到您的 enhanced
包装函数的 enhanced
属性:
from functools import partial
def decorator(func):
class EnhancedProperty:
# this allows function.enhanced() to work
def enhanced(self, *args, **kwargs):
print('enhanced', end=' ') # this output is for the demo below only
return func(*args, **kwargs)
# this allows function() to work
def __call__(self, *args, **kwargs):
return func(*args, **kwargs)
def __get__(self, obj, objtype):
class Enhanced:
# this allows x.function() to work
__call__ = partial(func, obj)
# this allows x.function.enhanced() to work
enhanced = partial(self.enhanced, obj)
return Enhanced()
return EnhancedProperty()
这样:
@decorator
def function():
print('function')
class X:
@decorator
def function(self):
print('method of %s' % self.__class__.__name__)
x = X()
function()
function.enhanced()
x.function()
x.function.enhanced()
会输出:
function
enhanced function
method of X
enhanced method of X
就像我在对@blhsing 发布的答案的评论中的意思的例子:
class EnhancedProperty:
def __init__(self, func):
self.func = func
def enhanced(self, *args, **kwargs):
return self.func(*args, **kwargs)
def __call__(self, *args, **kwargs):
return self.func(*args, **kwargs)
def __get__(self, obj, typ):
return Enhanced(self.func, obj, typ)
class Enhanced:
def __init__(self, func, obj, typ):
self.func = func
self.obj = obj
self.typ = typ
def __call__(self, *args, **kwargs):
return self.func.__get__(self.obj, self.typ)(*args, **kwargs)
def enhanced(self, *args, **kwargs):
return self.func(self.obj, *args, **kwargs)
def decorator(f):
return EnhancedProperty(f)
在 REPL 中:
In [2]: foo(8, -8)
Out[2]: 1040
In [3]: foo.enhanced(8, -8)
Out[3]: 1040
In [4]: Bar().baz('foo')
Out[4]: ('foo', 'foo')
In [5]: Bar().baz.enhanced('foo')
Out[5]: ('foo', 'foo')