Registry of Methods via Decorators

B

bayerj

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(object):

methods = []

@classmethod
def collect_methods(cls, method):
cls.methods.append(method.__name__)
return method

class dec2(decorated):

@collect_methods
def first_func(self):
pass

@collect_methods
def second_func(self):
pass


def main():
print dec2.methods

if __name__ == '__main__':
main()

This does not work and exits with "NameError: ("name 'collect_methods'
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?
 
B

Bruno Desthuilliers

bayerj said:
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(object):

methods = []

@classmethod
def collect_methods(cls, method):
cls.methods.append(method.__name__)
return method

class dec2(decorated):

@collect_methods
def first_func(self):
pass

@collect_methods
def second_func(self):
pass


def main():
print dec2.methods

if __name__ == '__main__':
main()

This does not work and exits with "NameError: ("name 'collect_methods'
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?

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
http://blog.ianbicking.org/a-conservative-metaclass.html

(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__(meta, class_name, bases, new_attrs)
cls.__classinit__.im_func(cls, new_attrs)
return cls
class Declarative(object):
__metaclass__ = DeclarativeMeta
def __classinit__(cls, new_attrs): pass

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

class dec2(MethodCollector):
def first_func(self):
pass

def second_func(self):
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__(cls, new_attrs):
cls.methods = [name for name, attr in new_attrs.items() \
if callable(attr) \
and getattr(attr, '_collected', False)]

class dec2(MethodCollector):
@collect
def first_func(self):
pass

@collect
def second_func(self):
pass

def not_collected(self):
pass



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

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


My 2 cents...
 
S

Steven Bethard

bayerj said:
I want to make a registry of methods of a class during creation.

I think you're going to need a metaclass for this, e.g.::
.... func.registered = True
.... return func
........ def __init__(cls, name, bases, classdict):
.... cls.methods = []
.... for name, value in classdict.iteritems():
.... if inspect.isfunction(value):
.... if hasattr(value, 'registered'):
.... cls.methods.append(name)
........ __metaclass__ = RegisterFuncs
.... @registered
.... def first_func(self):
.... pass
.... @registered
.... def second_func(self):
.... pass
....['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
 
M

Maric Michaud

Hi,

Le Jeudi 22 Juin 2006 15:32, bayerj a écrit :
I want to make a registry of methods of a class during creation.
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__.items() if isinstance(v,
types.FunctionType) ]
Out[44]: ['b']

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

Warning :

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

In [47]: list(isinstance(i, types.FunctionType) 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.
My
attempt was this

And that can't work,
""" classdecorators.py

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

class decorated(object):

methods = []

@classmethod
def collect_methods(cls, method):
cls.methods.append(method.__name__)
return method

class dec2(decorated):

@collect_methods
def first_func(self):
pass

@collect_methods
def second_func(self):
pass
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.collect_methods because the methods will
be appended to the decorated.methods list and not one list specific to dec2.
You neither can refer it as dec2.collect_methods because dec2 is still
undefined.
def main():
print dec2.methods

if __name__ == '__main__':
main()

This does not work and exits with "NameError: ("name 'collect_methods'
is not defined",)". Which is understandable due to the fact that the
class dec2 is not complete.
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_methods' 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
 
S

Stephan Diehl

bayerj said:
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(object):

methods = []

@classmethod
def collect_methods(cls, method):
cls.methods.append(method.__name__)
return method

class dec2(decorated):

@collect_methods
def first_func(self):
pass

@collect_methods
def second_func(self):
pass

replace '@collect_methods' with '@decorated.collect_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.
 
D

Duncan Booth

Stephan said:
replace '@collect_methods' with '@decorated.collect_methods'
and this will do what you want.

That is unlikely as it will keep a single list of methods for all classes
derived from decorated: calling decorated.collect_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).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,780
Messages
2,569,611
Members
45,268
Latest member
AshliMacin

Latest Threads

Top