1

Finally I fix this ask use code like this:(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__

That's not pyhonic right?I'm new guy to python

//////old

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)

There is error info:prt() missing 1 required positional argument: 'v'

It seems need a parameter named 'self' but how can i pass it?

//edit (2018-06-01)

Thanks to all.I ask this cause I try write a python web framework.And I want route to a method of class like below

app=MyFrm()

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

But existing do like below.It's no need or nobody to do this in python?

app = Flask(__name__)

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

Now I fixed this problem use @staticmethod

zhizunbao
  • 69
  • 4
  • What's the point of the `decorator` inner function? Why not just return `func` unaltered? – Martijn Pieters Jun 01 '18 at 18:22
  • I don't get it. What are the decorator and the `call` function supposed to do? – Aran-Fey Jun 01 '18 at 18:29
  • 1
    @Aran-Fey: imagine an event handler system, and the OP wants to register those methods in a class as event callbacks. – Martijn Pieters Jun 01 '18 at 18:30
  • @MartijnPieters That doesn't make any sense though. There's no `Test` instance that could be passed to the `prt` method. If that's what they're trying to do, it's fundamentally flawed. If you're right, the OP needs to explain which `Test` instance should be passed to `prt` and why. – Aran-Fey Jun 01 '18 at 18:31
  • @Aran-Fey: that is the common flaw in such systems, yes. You and I know that decorators are applied to the function before the class object is even created, and how binding works. To people new to Python that's all very magical and it is not always clear to them why the function passed to a decorator doesn't have `self` filled in. – Martijn Pieters Jun 01 '18 at 18:35

1 Answers1

4

Decorators are applied when the function is declared, so just before the class is created to which the functions are registered as methods.

So func passed into your decorator is not bound to an instance, and you can't just call it without explicitly passing in an instance. Moreover, you don't have access to the instance stored in t, that's created entirely outside of the decorator.

You'd have to explicitly pass along the instance to call the method on:

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

or register methods after creating an instance, in the __init__ method. Methods are bound when you look them up as an attribute on an instance, via the decriptor protocol. Only bound methods have a reference to the instance that is to be bound to 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>

If you do fix your registration to store bound methods, you'll need to take into account that the registration is now an extra reference to the instance, keeping it alive in memory even if all other references to the instance are removed. If this is an issue, you'd need to use weak references to the unbound function and the instance, not to the method, as methods are created dynamically and generally have no other references to them. See using python WeakSet to enable a callback functionality

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • If `d.call` takes an instance as the first argument that's forwarded to the decorated methods, that would mean that the decorator can only be used with methods (and no functions). I'm not convinced that this is a good solution. – Aran-Fey Jun 01 '18 at 18:37
  • @Aran-Fey: that's again an inherent problem with callback registrations for methods. Either you handle those explicitly (store weak refences to both the instance and the unbound function or live with hard reference and store the bound method to a specific instance at registration time), or you have to make sure the calling code passes in all arguments including an instance. – Martijn Pieters Jun 01 '18 at 18:40
  • @Aran-Fey: basically, you should never use a decorator to register handlers if you were thinking of using per-instance callbacks. If you are registering with a decorator on the class, then you must either have a registry to look up suitable instance(s) to bind the method to, or use static / class methods only. – Martijn Pieters Jun 01 '18 at 18:42
  • Ah, I misunderstood. I thought you were suggesting to rewrite `d.call` to *always* expect an instance as the first argument. My bad. I take back everything I said; that's a perfectly acceptable solution. – Aran-Fey Jun 01 '18 at 18:44