Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #104103 > unrolled thread

Re: Can I find the class of a method in a decorator.

Started byAntoon Pardon <antoon.pardon@rece.vub.ac.be>
First post2016-03-05 17:21 +0100
Last post2016-03-06 12:57 +1100
Articles 2 — 2 participants

Back to article view | Back to comp.lang.python

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  Re: Can I find the class of a method in a decorator. Antoon Pardon <antoon.pardon@rece.vub.ac.be> - 2016-03-05 17:21 +0100
    Re: Can I find the class of a method in a decorator. Steven D'Aprano <steve@pearwood.info> - 2016-03-06 12:57 +1100

#104103 — Re: Can I find the class of a method in a decorator.

FromAntoon Pardon <antoon.pardon@rece.vub.ac.be>
Date2016-03-05 17:21 +0100
SubjectRe: Can I find the class of a method in a decorator.
Message-ID<mailman.228.1457194876.20602.python-list@python.org>
Op 05-03-16 om 16:18 schreef Chris Angelico:
> On Sun, Mar 6, 2016 at 2:05 AM, Antoon Pardon
> <antoon.pardon@rece.vub.ac.be> wrote:
>> Using python 3.4/3.5
>>
>> Suppose I have the following class:
>>
>> class Tryout:
>>
>>     @extern
>>     def method(self, ...)
>>
>> Now how can I have access to the Tryout class in
>> the extern function when it is called with method
>> as argument
>>
>> def extern(f):
>>     the_class = ????
>>
>> f.__class doesn't work, if I write the following
>>
>> def extern(f)
>>     print(f.__class__)
>>
>> the result is: <class 'function'>, so that doesn't work.
>> Looking around I didn't find an other obvious candidate
>> to try. Anybody an idea?
> 
> At the time when the function decorator is run, there isn't any class.
> You could just as effectively create your function outside the class
> and then inject it (Tryout.method = method).
> 
> What is it you're trying to do? Would it be a problem to have a class
> decorator instead/as well?
> 
> ChrisA
> 

The idea is that some of these methods will be externally available
and others are not. So that I get an external string and can do
something of the following:

tryout = Tryout()

st = read_next_cmd()

if st in tryout.allowed:
    getattr(tryout, st)()
else:
    raise ValueError("%s: unallowed cmd string" % st)

And the way I wanted to populate Tryout.allowed as a class attribute
would have been with the decorator extern, which would just have put
the name of the method in the Tryout.allowed set and then return the function.

[toc] | [next] | [standalone]


#104118

FromSteven D'Aprano <steve@pearwood.info>
Date2016-03-06 12:57 +1100
Message-ID<56db8ea7$0$1585$c3e8da3$5496439d@news.astraweb.com>
In reply to#104103
On Sun, 6 Mar 2016 03:21 am, Antoon Pardon wrote:

> The idea is that some of these methods will be externally available
> and others are not. So that I get an external string and can do
> something of the following:
> 
> tryout = Tryout()
> 
> st = read_next_cmd()
> 
> if st in tryout.allowed:
>     getattr(tryout, st)()
> else:
>     raise ValueError("%s: unallowed cmd string" % st)
> 
> And the way I wanted to populate Tryout.allowed as a class attribute

The simplest way to do this is to forget about the decorator, since it's the
wrong tool for the job. The decorator doesn't have access to the class
itself, unless you pass it as an argument to the decorator. But you can't
do that *inside* the class, since it doesn't exist yet.

Instead, just keep a separate set of the names of methods. Do the simplest
thing which could possibly work, that is, maintain the set manually:

(All code following is untested.)


class Tryout:
    allowed = set(['spam', 'ham', 'cheese'])

    def spam(self): ...
    def ham(self): ...
    def eggs(self): ...
    def cheese(self): ...



That suggests a way to avoid having to manually add the method name to the
set: have a decorator that adds it to a known set.


def external(aset):
    def decorator(func):
        aset.add(func.__name__)
        return func
    return decorator


class Tryout:
    allowed = set()

    @external(allowed)
    def spam(self): ...

    @external(allowed)
    def ham(self): ...

    def eggs(self): ...

    @external(allowed)
    def cheese(self): ...




Which leads us to our next improvement:

class Tryout:
    allowed = set()
    allow = functools.partial(external, allowed)

    @allow
    def spam(self): ...

    @allow
    def ham(self): ...

    def eggs(self): ...

    @allow
    def cheese(self): ...

    del allow




But there's another approach. A decorator cannot easily know anything about
the class that the method will belong to, but it can tag the method itself:


def allow(func):
    func.allowed = True
    return func


class Tryout:

    @allow
    def spam(self): ...

    @allow
    def ham(self): ...

    def eggs(self): ...

    @allow
    def cheese(self): ...



tryout = Tryout()
st = read_next_cmd()
method = getattr(tryout, st)
if method.allowed:
    method()
else:
    raise ValueError("%s: unallowed cmd string" % st)


Obviously you don't write that out in full each time, you factor it into a
method or function.




-- 
Steven

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web