Python 类方法装饰器

Python 类方法装饰器,python,python-3.x,Python,Python 3.x,我试图复制内置属性类/装饰器的功能;我想说的一个非常基本的例子是: #如果满足条件,则运行第一个函数,否则运行第二个函数。 @神速 def test(): 打印(1、2、3、4) @test.else() def test(): 打印(5、6、7、8) 以下是我目前掌握的情况: 导入检查 类godspeed_class(): 定义初始化__( 自己 func, args, 夸尔斯, 价值 ): self.func=func self.args=args self.kwargs=kwargs 自

我试图复制内置
属性
类/装饰器的功能;我想说的一个非常基本的例子是:

#如果满足条件,则运行第一个函数,否则运行第二个函数。
@神速
def test():
打印(1、2、3、4)
@test.else()
def test():
打印(5、6、7、8)
以下是我目前掌握的情况:

导入检查
类godspeed_class():
定义初始化__(
自己
func,
args,
夸尔斯,
价值
):
self.func=func
self.args=args
self.kwargs=kwargs
自我价值=价值
定义呼叫(自我):
如果自我价值观:
self.func(*self.args,**self.kwargs)
其他:
self.else\u func(*self.else\u args,**self.else\u kwargs)
def else_uuz(self,*args,**kwargs):
def包装器(func):
self.else_func=func
self.else_args=args
self.else_kwargs=kwargs
返回包装器
def航速(*args,值=0,**kwargs):
def包装器(func):
_=godspeed_类(func、args、kwargs、value)
inspect.stack(1)[1][0].f_globals[func.\uu name\uu]=_
返回包装器
我已经知道如何实现条件解析,但是我在类中的
else\uu
decorator下存储函数时遇到了问题,因此如果条件不满足,我可以调用它

此外,尽管将新类直接注入全局名称空间,但当我运行
print(test)
时,它告诉我它是一个
NoneType
对象


注:代码已更新;但是,它仍然会给我“
NoneType
object”错误。

您需要更改两个
包装器
函数以返回一个可调用的对象,可能是类的实例。否则,您将使用
None
作为方法的值,因为decorator语法将返回值分配给修饰函数的名称,这意味着即使
inspect
hack有效,它也会被覆盖

我建议:

class godspeed_class():
    ...                       # __init__ and __call__ can remain the same

    def else_(self, *args, **kwargs):
        def wrapper(func):
            self.else_func = func
            self.else_args = args
            self.else_kwargs = kwargs
            return self                       # add return here
        return wrapper

def godspeed(*args, value = 0, **kwargs):
    def wrapper(func):
        return godspeed_class(func, args, kwargs, value) # and here (rather than inspect stuff)
    return wrapper
这将通过顶级的
test
功能完成示例的工作。如果希望能够修饰方法,还需要向类中添加一个
\uuuu get\uuu
方法来添加绑定行为(否则将无法将
self
参数传递到包装的方法中)

使用
wrapper
作为名称有点误导,因为内部函数是这里使用的实际装饰器(顶级
godspeed
函数和
else\ucode>方法是装饰器工厂)。通常,您使用
wrapper
作为装饰程序返回的函数的名称(但您使用的是类)


我还需要注意的是,将函数的参数传递给装饰器工厂,而不是让
\uuuu调用\uuu
接受传递给相关函数的参数,这有点奇怪。对于留下可调用(而不是像
属性那样工作方式不同的东西)的装饰程序来说,戏剧性地改变函数的调用约定是有点不寻常的,因为调用程序可能很难知道他们希望传递什么参数,如果函数签名不再具有代表性。

装饰器没有什么神奇之处。基本上,
@decorator
语法只是语法糖,因此:

@mydecorator
def func(): 
    pass
这只是一条方便的捷径

def func():
    pass

func = mydecorator(func)
瞧,“decorator”是一个可调用对象,它接受一个可调用对象作为输入,并返回一个可调用对象(好吧,它至少应该返回一个可调用对象-实际上,您可以返回任何东西,但这样会打破每个人的期望)

最常见的情况是,decorator被编写为一个简单的函数,返回对修饰函数的闭包:

def trace(func):
    def wrapper(*args, **kw):
        result = func(*args, **kw)
        print("{}({}, {}) => {}". format(func, args, kw, result))
        return result

    return wrapper


@trace
def foo(x):
    return 42 * x
但是(因为闭包是穷人的类,而类是穷人的闭包),您也可以将其实现为可调用类,在这种情况下,初始值设定项将接收修饰func,而修饰func将被实例替换:

class trace(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kw):
        result = self.func(*args, **kw)
        print("{}({}, {}) => {}". format(self.func, args, kw, result))
        return result



@trace
def foo(x):
    return 42 * x
然后你就有了“参数化”的装饰器——可以接受参数的装饰器。在这种情况下,您需要两级间接寻址,顶级间接寻址(用作装饰器的间接寻址)返回实际的装饰器(接收函数的间接寻址),即:

我将基于类的实现作为练习留给读者;-)

现在在您的例子中,
test
最终成为
None
,这一事实很简单,因为您的包装函数忘记了返回它应该返回的
godspeed\u类
实例(而不是弄乱了函数的
f_全局
,正如您所注意到的,它没有按预期工作)

由于您没有清楚地解释您在这里要实现的目标(“类似于
属性的属性
”不是一个合适的规范),因此很难提供有效的解决方案,但作为起点,您可能希望修复
godspeed
函数,使其按预期运行:

def godspeed(*args, value = 0, **kwargs):
    def wrapper(func):
        return godspeed_class(func, args, kwargs, value)

    return wrapper

其他人不应该回归自我吗?为什么包装器不返回实例呢?不幸的是,由于某些原因,我还没有完全理解decorators;你能给我解释一下这两种方法都能做些什么吗?然后我建议你对它们做一个更有条理的介绍,从一些不太复杂的事情开始,直到你明白发生了什么。目前没有理由期望您的代码能够正常工作,而inspect的使用令人费解;对不起,我花了一些时间才理解这个评论。我使用inspect用一个测试类实例重写测试函数;这和
属性
不一样吗?不,不。属性是a。谢谢!实际上,我正在使用一个函数,而且我不知道如何动态实例化类,而不用像
instance=class()
或类似的方法;归还它们似乎是我最好的选择!对不起
def godspeed(*args, value = 0, **kwargs):
    def wrapper(func):
        return godspeed_class(func, args, kwargs, value)

    return wrapper