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