dinamically altering a function

vegetax vegeta.z at gmail.com
Sat Mar 12 22:09:58 EST 2005


Bengt Richter wrote:

> 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

Ok , the complete use case is :

def fun1(a,b,obj = None):
   isSuplied = 1
   if not obj : 
      obj = generateObject()
      isSuplied = 0
   
   <do something with obj>

   if not isSuplied : obj.dispose()

def fun2(c,obj = None):
   isSuplied = 1
   if not obj : 
      obj = generateObject()
      isSuplied = 0
   
   <do something else with obj>
   
   if not isSuplied : obj.dispose()

So , maybe i will need to define fun3 later,i dont know , but fun3 also will
work with "obj".
So the thing is to try to factorize the code,so that it is in the decorator:

def dec(func):
   def wrapper(*arg,**kw):
      obj = kw.get('obj',None)
      isSuplied = 1
      if not obj :
         obj = generateObject()
         kw['obj'] = obj
         isSuplied = 0
      
      res = func(*arg,**kw)
      if not isSuplied : obj.dispose()
      return res

   return wrapper

so the previous function declarations, will look like:

@dec
def fun1(a,b,obj = None):
   <do something with obj>

@dec
def fun2(c,obj = None):
   <do something with obj>

but i have to define obj = None in each function,but the functions should
know they will have an obj instance available, so the byte code hack would
be :

def dec(func):
   def wrapper(*arg,**kw):
      obj = kw.get('obj',None)
      isSuplied = 1
      if not obj :
         obj = generateObject()
         isSuplied = 0
      
      << BIND OBJ TO THE FUNCTION LOCAL NAMESPACE >> 

      res = func(*arg,**kw)
      if not isSuplied : obj.dispose()
      return res

   return wrapper

so the functions knowing they will be altered(added an instance of obj),they
will just need to define their own arguments only.

@dec
fun1(a,b) : obj.result = a + b
@dec
fun2(c) : obj.result = c * 4

So the duplication would be removed.

But i realized the best aproach,is a callable class that provides that
infrastructure, or not? =P






More information about the Python-list mailing list