Welcome, guest | Sign In | My Account | Store | Cart

This is meant to be a method for writing decorators very easily.

Python, 68 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
class AttributeHelper(object):
    def __new__(typ, *attr_args, **attr_kwargs):
        self = object.__new__(typ)

        def f2(orig_func):
            def f3(*func_args, **func_kwargs):
                # Take argument list for original function
                return self(*func_args, **func_kwargs)

            f3.func_name = orig_func.func_name
            self.orig_func = orig_func
            self.new_func = f3
            self.__init__(*attr_args, **attr_kwargs)                
            return f3
        
        return f2
        
    def __init__(self, *attr_args, **attr_kwargs):
        return self.on_init(*attr_args, **attr_kwargs)
            
    def __call__(self, *args, **kwargs):
        return self.on_call(*args, **kwargs)
        
    def on_call(self, *args, **kwargs):
        return self.orig_func(*args, **kwargs)
   
##### Example
class DebugAttribute(AttributeHelper):
    def on_init(self, msg):
        self.msg = msg
        self.log = []
        self.new_func.print_log = self.print_log
        
    def on_call(self, *args, **kwargs):
        result = super(DebugAttribute, self).on_call(*args, **kwargs)
        self.log.append((args, kwargs,result))
        return result
        
    def print_log(self):
        name = self.orig_func.func_name
        for args, kwargs, result in self.log:
            join_str = ', '
            if args==():
                args_str = ''
                join_str=''
            else:
                args_str = ', '.join(str(i) for i in args)
                
            if kwargs=={}:
                kwargs_str = ''
                join_str=''
            else:
                kwargs_str = ', '.join('%s=%s'%(str(k),str(v)) for k,v in kwargs.items())
                
            result_str = str(result)
                
            print '%s(%s%s%s) -> %s'%(name, args_str, join_str, kwargs_str, result_str)
                
        
if __name__=='__main__':
    @DebugAttribute('hi')
    def hi(a,b):
        return a+b
        
    hi(1,2)
    hi(4,5)
    hi(a=6,b=9)
    hi.print_log()

One of the major problems with decorators is that you can easily end up with up to 3 function declerations: 1) Getting the decorator arguments 2) Getting the original function 3) The actual call to the function

This class makes it much simpler and should allow for less hassle in writing decorators. Notice that even if you don't require any arguments for your attribute you will still need to use: @attr() def f(): pass

instead of: @attr def f(): pass

This class can definatly be improved in many ways: * Make the outer class look more like the original function (argument list, filename, docstring, etc...) * Automatically detect if on_init has any arguments and then do not require @attr() * Any other suggestions would be gladly added

2 comments

Ori Peleg 18 years, 8 months ago  # | flag

Nice idea! Haven't tried the recipe yet, but compacting the 3-function idiom sounds useful.

Neil Toronto 17 years, 6 months ago  # | flag

This is overly complex. Try this:

class decoratorargs(object):
    def __new__(typ, *attr_args, **attr_kwargs):
        def decorator(orig_func):
            self = object.__new__(typ)
            self.__init__(orig_func, *attr_args, **attr_kwargs)
            return self

        return decorator

class decorator(decoratorargs):
    def __init__(self, arg1, arg2, ...):
        ...

    def __call__(self, ...):
        ...
Created by Daniel Brodie on Mon, 18 Jul 2005 (PSF)
Python recipes (4591)
Daniel Brodie's recipes (5)

Required Modules

  • (none specified)

Other Information and Tasks