【问题标题】:Python Is there a way to use decorator wrap a function of class?Python有没有办法使用装饰器包装类的功能?
【发布时间】:2018-06-01 18:16:59
【问题描述】:

最后我修复了这个问题,使用这样的代码:(2018-06-03)

class Main:
    @app.route('^/$')
    def default(self):
        return 'hello from class ccc'

module=sys.modules[func.__module__]
cls=getattr(module,func.__qualname__.replace('.'+func.__name__,''))      
ins=cls()
m=getattr(cls,func.__name__)    
resp.body=m(cls) #func.__module__+'.'+func.__qualname__+' func:'+func.__name__

这不是 pyhonic 对吧?我是 python 新手

//////旧

class D:
    def __init__(self):
        self.handlers={}

    def check(self,func):
        self.handlers['h']=func

        def decorator(*args,**kwargs):
            return func(*args,**kwargs)
        return decorator

    def call(self,p):
        return self.handlers['h'](p)

d=D()

class Test:
    @d.check
    def prt(self,v):
        print(v)

t=Test()
d.call(123)

有错误信息:prt() missing 1 required positional argument: 'v'

似乎需要一个名为“self”的参数,但我该如何传递呢?

//编辑(2018-06-01)

谢谢大家。我问这个是因为我尝试编写一个 python web 框架。我想要路由到如下类的方法

app=MyFrm()

class Controller:
    @app.route('/')
    def hello():
        return 'hello world'

但现有的就像下面那样。在python中不需要或没有人这样做?

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

现在我使用@staticmethod 解决了这个问题

【问题讨论】:

  • decorator 内部函数的意义何在?为什么不直接返回func
  • 我不明白。装饰器和call 函数应该做什么?
  • @Aran-Fey:想象一个事件处理系统,并且 OP 想要在一个类中注册这些方法作为事件回调。
  • @MartijnPieters 但这没有任何意义。没有可以传递给prt 方法的Test 实例。如果这就是他们想要做的事情,那从根本上来说是有缺陷的。如果你是对的,OP 需要解释应该将哪个 Test 实例传递给 prt 以及为什么。
  • @Aran-Fey:这是此类系统中的常见缺陷,是的。你我都知道装饰器在类对象创建之前就被应用到函数上,以及绑定是如何工作的。对于 Python 新手来说,这一切都非常神奇,他们并不总是清楚为什么传递给装饰器的函数没有填写 self

标签: python


【解决方案1】:

装饰器在函数被声明时应用,所以就在创建函数注册为方法的类之前。

所以func 传递给你的装饰器没有绑定到一个实例,你不能在没有显式传递实例的情况下调用它。此外,您无权访问存储在 t 中的实例,该实例完全在装饰器之外创建。

您必须显式传递实例才能调用方法:

t = Test()
d.call(t, 123)

或在__init__ 方法中创建实例之后 注册方法。当您通过decriptor protocol 将它们作为实例上的属性查找时,方法将被绑定。只有绑定的方法才会引用要绑定到self 的实例:

>>> class Foo:
...     def bar(self):
...         return self
...
>>> Foo.bar  # unbound
<function Foo.bar at 0x108d38f28>
>>> Foo.bar()  # no self to bind to
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: bar() missing 1 required positional argument: 'self'
>>> instance = Foo()
>>> instance.bar  # bound method
<bound method Foo.bar of <__main__.Foo object at 0x109a916d8>>
>>> instance.bar.__self__ is instance  # to the instance
True
>>> instance.bar()  # so the instance is passed in for self
<__main__.Foo object at 0x109a916d8>
>>> Foo.bar(instance)  # or you can do it manually
<__main__.Foo object at 0x109a916d8>

如果您确实修复您的注册以存储绑定方法,您需要考虑到注册现在是对实例的额外引用,即使所有其他对实例的引用被删除。如果这是一个问题,您需要使用对未绑定函数和实例的弱引用,而不是方法,因为方法是动态创建的,通常没有其他对它们的引用。见using python WeakSet to enable a callback functionality

【讨论】:

  • 如果d.call 将实例作为第一个参数转发给装饰方法,这意味着装饰器只能与方法一起使用(不能与函数一起使用)。我不相信这是一个好的解决方案。
  • @Aran-Fey:这又是方法回调注册的固有问题。您要么显式处理这些(存储对实例和未绑定函数的弱引用,要么使用硬引用并在注册时将绑定方法存储到特定实例),或者您必须确保调用代码传递所有参数,包括一个实例。
  • @Aran-Fey:基本上,如果您正在考虑使用每个实例的回调,则永远不应该使用装饰器来注册处理程序。如果你在类上注册一个装饰器,那么你必须有一个注册表来查找合适的实例来绑定方法,或者只使用静态/类方法。
  • 啊,我误会了。我以为您建议将 d.call 重写为 always 期望实例作为第一个参数。我的错。我收回我所说的一切;这是一个完全可以接受的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-06-15
  • 2019-06-16
  • 2012-02-29
  • 2012-05-11
  • 2020-12-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多