""" 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...
--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in 'on...@xiludom.gro'.split('@')])"
I think you're going to need a metaclass for this, e.g.::
>>> import inspect
>>> def registered(func):
... func.registered = True
... return func
...
>>> class RegisterFuncs(type):
... 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)
...
>>> class C(object):
... __metaclass__ = RegisterFuncs
... @registered
... def first_func(self):
... pass
... @registered
... def second_func(self):
... pass
...
>>> C.methods
['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
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
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.
> 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).