dinamically altering a function

Bengt Richter bokr at oz.net
Sat Mar 12 21:08:23 EST 2005


On Sat, 12 Mar 2005 18:19:36 -0400, vegetax <vegeta.z at gmail.com> wrote:

>Steven Bethard wrote:
>
>> vegetax wrote:
>>> I i need a decorator that adds a local variable in the function it
>>> decorates, probably related with nested scopes, for example:
>>> 
>>> def dec(func):
>>>     def wrapper(obj = None):
>>>         if not obj : obj = Obj()
>>>   <bind obj to func>
>>>  return func()
>>> 
>>>     return wrapper()
>>> 
>>> @dec()
>>> def fun(b):
>>>     obj.desc = 'marked'
>>>     obj.b = b
>>>     return obj
>>> 
>>> so the call to fun : fun(obj = myobj,'b argument')
>>> or fun('b argument')
>>> 
>>> So the function "fun" assumes it has an obj instance and other instance
>>> objects computed by the decorator, this decorator will be like a generic
>>> factory for this kind of functions,which depends on the decorator to
>>> work.
>> 
>> For a byte-code hack that does something similar, see the recent thread:
>> 
>> http://mail.python.org/pipermail/python-list/2005-March/270324.html
>> 
>> It can do something like:
>> 
>> py> class Object(object):
>> ...     pass
>> ...
>> py> @presets.presets(obj=Object())
>> ... def fun(b):
>> ...     obj.desc = "marked"
>> ...     obj.b = b
>> ...     return obj
>> ...
>> py> fun(1)
>> <__main__.Object object at 0x01162BD0>
>> py> fun(1).b
>> 1
>> 
>> But note that you then only have a single instance for all calls to the
>> function:
>> 
>> py> fun(1) is fun(2)
>> True
>
>Interesting hack, but the functions must not share the same object.Maybe i
>could make my version of that decorator,i will check it.
What do you mean "function_s_" [plural] ? I see only one function named "fun".
An ordinary decorator will only modify that single function, so if you want
more than one function instance, IWT you would want a factory function that
can produce multiple function instances, tailored as you like. You could
use a decorator to modify a template function within the factory, and return
the result. But you might no need a decorator if you just make your template
function refer to stuff passed into the factory, i.e., as cell variables.

Will your template function know the names of dynamic things it will use?
If not, what kind of protocol will you use to discover what's available?
If it does know the names (like "obj") then you could just write something like


 >>> def mkfun(obj=None):
 ...     if obj is None: obj = Obj()
 ...     def fun(b):
 ...         obj.desc = 'marked'
 ...         obj.b = b
 ...         return obj
 ...     return fun
 ...
 >>> fun = mkfun()
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
   File "<stdin>", line 2, in mkfun
 NameError: global name 'Obj' is not defined

oops

 >>> class Obj(object): pass
 ...
 >>> fun = mkfun()
 >>> fun2 = mkfun()
 >>> fun(1)
 <__main__.Obj object at 0x02EF15CC>
  >>> fun(1).b
 1
 >>> fun(2).b
 2
 >>> fun2(3)
 <__main__.Obj object at 0x02EF162C>
 >>> fun2(3).b
 3
 >>> fun(1) is fun2(2)
 False
 >>> fun(1) is fun(2)
 True

The code of the function the factory produces is:

 >>> dis.dis(mkfun())
   4           0 LOAD_CONST               1 ('marked')
               3 LOAD_DEREF               0 (obj)
               6 STORE_ATTR               1 (desc)

   5           9 LOAD_FAST                0 (b)
              12 LOAD_DEREF               0 (obj)
              15 STORE_ATTR               2 (b)

   6          18 LOAD_DEREF               0 (obj)
              21 RETURN_VALUE

We could use the byte code hack to change the code not to use
LOAD_DEREF cell vars, but preset and use a local variable instead:

 >>> from ut.presets import presets
 >>> def mkfun(obj=None):
 ...     if obj is None: obj = Obj()
 ...     @presets(obj=obj)
 ...     def fun(b):
 ...         obj.desc = 'marked'
 ...         obj.b = b
 ...         return obj
 ...     return fun
 ...
 >>> dis.dis(mkfun())
   3           0 LOAD_CONST               2 (<__main__.Obj object at 0x02EF728C>)
               3 STORE_FAST               1 (obj)

   5           6 LOAD_CONST               1 ('marked')
               9 LOAD_FAST                1 (obj)
              12 STORE_ATTR               1 (desc)

   6          15 LOAD_FAST                0 (b)
              18 LOAD_FAST                1 (obj)
              21 STORE_ATTR               2 (b)

   7          24 LOAD_FAST                1 (obj)
              27 RETURN_VALUE

Not sure that gains anything significant.

>
>> Have you considered using OO here?  You might find that this is more
>> easily written as:
>> 
>> py> class Object(object):
>> ...     pass
>> ...
>> py> class fun(object):
>> ...     def __new__(self, *args):
>> ...         if len(args) == 2:
>> ...             obj, b = args
>> ...         elif len(args) == 1:
>> ...             obj, [b] = Object(), args
>> ...         else:
>> ...             raise TypeError
>> ...         obj.desc = "marked"
>> ...         obj.b = b
>> ...         return obj
>> ...
>> py> myobj = Object()
>> py> fun(myobj, 2)
>> <__main__.Object object at 0x01162E30>
>> py> myobj.b
>> 2
>> py> obj = fun(1)
>> py> obj.b
>> 1
>> 
>> This doesn't use any bytecode hacks, but I'm still not certain it's
>> really the way to go.  What is it you're trying to write this way?
Yes, that is a good question ;-)

>
>OO doesnt work here,i have factored to classes with inheritance but it looks
>clumsy and it is clumsy to use, this things are in nature,functions.
>
>What i want is to declare in the decorator some code that is common to all
>these functions, so the functions assume that the decorator will be there
>and wont need to duplicate the code provided by it, and the functions are
>not known ahead of time, it has to be dynamic.
Still not sure what you mean by "the functions are not known ahead of time."

Please pretend things worked the way you want, and post an example. Maybe we can
make it work.

Regards,
Bengt Richter



More information about the Python-list mailing list