Registry of Methods via Decorators

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • bayerj

    Registry of Methods via Decorators

    I want to make a registry of methods of a class during creation. My
    attempt was this

    """ classdecorators .py

    Author: Justin Bayer
    Creation Date: 2006-06-22
    Copyright (c) 2006 Chess Pattern Soft,
    All rights reserved. """

    class decorated(objec t):

    methods = []

    @classmethod
    def collect_methods (cls, method):
    cls.methods.app end(method.__na me__)
    return method

    class dec2(decorated) :

    @collect_method s
    def first_func(self ):
    pass

    @collect_method s
    def second_func(sel f):
    pass


    def main():
    print dec2.methods

    if __name__ == '__main__':
    main()

    This does not work and exits with "NameError: ("name 'collect_method s'
    is not defined",)". Which is understandable due to the fact that the
    class dec2 is not complete.

    Anyone can give me a hint how to work around this?

  • Bruno Desthuilliers

    #2
    Re: Registry of Methods via Decorators

    bayerj wrote:[color=blue]
    > I want to make a registry of methods of a class during creation. My
    > attempt was this
    >
    > """ classdecorators .py
    >
    > Author: Justin Bayer
    > Creation Date: 2006-06-22
    > Copyright (c) 2006 Chess Pattern Soft,
    > All rights reserved. """
    >
    > class decorated(objec t):
    >
    > methods = []
    >
    > @classmethod
    > def collect_methods (cls, method):
    > cls.methods.app end(method.__na me__)
    > return method
    >
    > class dec2(decorated) :
    >
    > @collect_method s
    > def first_func(self ):
    > pass
    >
    > @collect_method s
    > def second_func(sel f):
    > pass
    >
    >
    > def main():
    > print dec2.methods
    >
    > if __name__ == '__main__':
    > main()
    >
    > This does not work and exits with "NameError: ("name 'collect_method s'
    > is not defined",)". Which is understandable due to the fact that the
    > class dec2 is not complete.
    >
    > Anyone can give me a hint how to work around this?[/color]

    If you insist on doing black-magic (else go directly to the end of this
    post), here's a way to do it, based on Ian Bicking's __classinit__ recipe


    (BTW, Ian, many many thanks for this trick - I really love it).

    class DeclarativeMeta (type):
    def __new__(meta, class_name, bases, new_attrs):
    cls = type.__new__(me ta, class_name, bases, new_attrs)
    cls.__classinit __.im_func(cls, new_attrs)
    return cls
    class Declarative(obj ect):
    __metaclass__ = DeclarativeMeta
    def __classinit__(c ls, new_attrs): pass

    class MethodCollector (Declarative):
    def __classinit__(c ls, new_attrs):
    cls.methods = [name for name, attr in new_attrs.items () \
    if callable(attr)]

    class dec2(MethodColl ector):
    def first_func(self ):
    pass

    def second_func(sel f):
    pass


    If you want to choose which methods to collect, then it's just a matter
    of adding a simple decorator and a test in MethodCollector .__classinit__:

    def collect(func):
    func._collected = True
    return func


    class MethodCollector (Declarative):
    def __classinit__(c ls, new_attrs):
    cls.methods = [name for name, attr in new_attrs.items () \
    if callable(attr) \
    and getattr(attr, '_collected', False)]

    class dec2(MethodColl ector):
    @collect
    def first_func(self ):
    pass

    @collect
    def second_func(sel f):
    pass

    def not_collected(s elf):
    pass



    *BUT* is it really useful to go thru all this mess ?

    class DeadSimple(obje ct):
    @classmethod
    def methods(cls):
    return [name for name in dir(cls) \
    if not name.startswith ('__') \
    and callable(getatt r(cls, name))]


    My 2 cents...
    --
    bruno desthuilliers
    python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
    p in 'onurb@xiludom. gro'.split('@')])"

    Comment

    • Steven Bethard

      #3
      Re: Registry of Methods via Decorators

      bayerj wrote:[color=blue]
      > I want to make a registry of methods of a class during creation.[/color]

      I think you're going to need a metaclass for this, e.g.::
      [color=blue][color=green][color=darkred]
      >>> import inspect
      >>> def registered(func ):[/color][/color][/color]
      .... func.registered = True
      .... return func
      ....[color=blue][color=green][color=darkred]
      >>> class RegisterFuncs(t ype):[/color][/color][/color]
      .... def __init__(cls, name, bases, classdict):
      .... cls.methods = []
      .... for name, value in classdict.iteri tems():
      .... if inspect.isfunct ion(value):
      .... if hasattr(value, 'registered'):
      .... cls.methods.app end(name)
      ....[color=blue][color=green][color=darkred]
      >>> class C(object):[/color][/color][/color]
      .... __metaclass__ = RegisterFuncs
      .... @registered
      .... def first_func(self ):
      .... pass
      .... @registered
      .... def second_func(sel f):
      .... pass
      ....[color=blue][color=green][color=darkred]
      >>> C.methods[/color][/color][/color]
      ['first_func', 'second_func']

      If you just want to store *all* method names, you can dispense with the
      @registered decorator and the hasattr() check.

      STeVe

      Comment

      • Maric Michaud

        #4
        Re: Registry of Methods via Decorators

        Hi,

        Le Jeudi 22 Juin 2006 15:32, bayerj a écrit :[color=blue]
        > I want to make a registry of methods of a class during creation.[/color]
        Why ? you already have them in dec2.__dict__ :

        In [42]: import types

        In [43]: class a :
        ....: def b(self) : return
        ....: @classmethod
        ....: def c(self) : return
        ....:
        ....:

        In [44]: [ k for k, v in a.__dict__.item s() if isinstance(v,
        types.FunctionT ype) ]
        Out[44]: ['b']

        In [45]: [ k for k, v in a.__dict__.item s() if isinstance(v, classmethod) ]
        Out[45]: ['c']

        Warning :

        In [46]: list(isinstance (i, types.MethodTyp e) for i in (a.b, a().b,
        a.__dict__['b']))
        Out[46]: [True, True, False]

        In [47]: list(isinstance (i, types.FunctionT ype) for i in (a.b, a().b,
        a.__dict__['b']))
        Out[47]: [False, False, True]

        I would prefer write some inspection method that retrieve all these infos.
        [color=blue]
        > My
        > attempt was this[/color]

        And that can't work,
        [color=blue]
        >
        > """ classdecorators .py
        >
        > Author: Justin Bayer
        > Creation Date: 2006-06-22
        > Copyright (c) 2006 Chess Pattern Soft,
        > All rights reserved. """
        >
        > class decorated(objec t):
        >
        > methods = []
        >
        > @classmethod
        > def collect_methods (cls, method):
        > cls.methods.app end(method.__na me__)
        > return method
        >
        > class dec2(decorated) :
        >
        > @collect_method s
        > def first_func(self ):
        > pass
        >
        > @collect_method s
        > def second_func(sel f):
        > pass
        >[/color]
        This is trying to do :
        first_func = collect_methods (first_fun)
        but collect_methods doesn't exists in the global namespace (indeed you got a
        NameError exception).

        You can't reference it as decorated.colle ct_methods because the methods will
        be appended to the decorated.metho ds list and not one list specific to dec2.
        You neither can refer it as dec2.collect_me thods because dec2 is still
        undefined.
        [color=blue]
        >
        > def main():
        > print dec2.methods
        >
        > if __name__ == '__main__':
        > main()
        >
        > This does not work and exits with "NameError: ("name 'collect_method s'
        > is not defined",)". Which is understandable due to the fact that the
        > class dec2 is not complete.[/color]
        Not exactly.
        At any moment in a python program, there are two and only two scope, global
        and local, global is usually the module level scope (where
        no 'collect_method s' exists), and, in the case of a class definition, local
        is the class __dict__ (the local namespace is not same the class and its
        method).


        But I'm not sure of what you really want : a list of all decorated methods of
        all subclasses of a class, or a list of marked method in each class ?

        --
        _____________

        Maric Michaud
        _____________

        Aristote - www.aristote.info
        3 place des tapis
        69004 Lyon
        Tel: +33 426 880 097

        Comment

        • Stephan Diehl

          #5
          Re: Registry of Methods via Decorators

          bayerj schrieb:[color=blue]
          > I want to make a registry of methods of a class during creation. My
          > attempt was this
          >
          > """ classdecorators .py
          >
          > Author: Justin Bayer
          > Creation Date: 2006-06-22
          > Copyright (c) 2006 Chess Pattern Soft,
          > All rights reserved. """
          >
          > class decorated(objec t):
          >
          > methods = []
          >
          > @classmethod
          > def collect_methods (cls, method):
          > cls.methods.app end(method.__na me__)
          > return method
          >
          > class dec2(decorated) :
          >
          > @collect_method s
          > def first_func(self ):
          > pass
          >
          > @collect_method s
          > def second_func(sel f):
          > pass[/color]

          replace '@collect_metho ds' with '@decorated.col lect_methods'
          and this will do what you want.

          But keep in mind, that the 'methods' list in decorated will be used for
          all derived classes.

          Comment

          • Duncan Booth

            #6
            Re: Registry of Methods via Decorators

            Stephan Diehl wrote:
            [color=blue]
            > replace '@collect_metho ds' with '@decorated.col lect_methods'
            > and this will do what you want.[/color]

            That is unlikely as it will keep a single list of methods for all classes
            derived from decorated: calling decorated.colle ct_methods will pass
            decorated as the cls parameter. What the OP wants it a separate list for
            each subclass.

            The way to do that of course is as others have suggested, just stick an
            attribute on each decorated function and then collect_methods goes through
            the class dict when it is called and picks out the correct methods. It
            could even build a list cached on the class at that time if it needs to
            (although the speedup is unlikely to be significant over just iterating
            through all the methods picking out the marked ones).

            Comment

            Working...