create dynamic functions keeping state - python

Python beginner question. Say you want to dynamically create a function that keeps some state (in the code below an integer i). Then as the function defined is an object, we could use it later on. In the following code, I add the functions to a list, and each call to print(fn(0)) should result in 0 1 2 3 4, but instead I get 4 4 4 4 4 as if only the latest value of i is used.
fns = []
for i in range(5):
def fn(x):
return x + i
fns += [fn]
print(fns)
for fn in fns:
print(fn(0))
quit()
Is it possible to accomplish in Python what this code tries to do?

To work around the late binding issue, you can use default keyword parameter:
for i in range(5):
def fn(x, i=i): # <-----
return x + i
....

A function that retains its own state? I would do it like this:
class func:
# can behave as a function, but can also hold its own value
def __init__(self, value=0):
self.value = value
# this function is the actual 'function' that is called each time
def __call__(self, arg):
self.value += arg
return self.value
fns = []
for i in range(5):
# create new function, and give it a starting value of i
fn = func(i)
fns += [fn]
for fn in fns:
print( fn(0) )
The result is 0 1 2 3 4

Related

What is the syntax for the input for a def function with multiple nested functions?

I'm learning Python right now and I am just trying to get to grips with all of the syntax options.
Currently, the only thing that I can't seem to google up is what to do if I for some reason want to define a function which contains multiple other defines.
While I understand what to do if there's only 1 define inside the the larger define (val = f()(3,4) returns 7 if you exclude the second def below), I don't know how to correctly use the function below.
If it's possible, what is the syntax for a def function with an arbitrary amount of defined functions within it?
Code:
def f():
def x(a,b):
return a + b
return x
def y(c,d):
return c + d
return y
val = f()(3,4)(5,6)
print(val)
I expected the above to return either (7,11) or 11. However, it returns 'int object is not callable'
When you write val = f()(3,4)(5,6), you want f to return a function that also returns a function; compare with the simpler multi-line call:
t1 = f()
t2 = t1(3,4)
val = t2(5,6)
The function f defines and returns also has to define and return a function that can be called with 2 arguments. So, as #jonrsharpe said, you need more nesting:
def f():
def x(a, b):
def y(c, d):
return c + d
return y
return x
Now, f() produces the function named x, and f()(3,4) produces the function named y (ignoring its arguments 3 and 4 in the process), and f()(3,4)(5,6) evaluates (ultimately) to 5 + 6.

Deep copy index integer using lambda

func = []
for value in range(5):
func.append(lambda: print_index(value))
def print_index(index_to_print):
print(index_to_print)
for a in func:
a()
When I run the code above, the output is
4
4
4
4
4
Why is it not ?
0
1
2
3
4
What can I do to make it like the above.
I have tried importing copy and using copy.deepcopy(index). I didn't work probably since index is an integer.
Is lambda part of the reason it is not working.
Thank you for your help!
Not quite sure what you are trying to achieve but the reason that it is printing all 4s is because Python uses dynamic name resolution of variables, that is the value of value when the functions are executed (not when it is declared) is used.
If you really need a function to print the value then you need to create a closure, which means creating the function inside another function, e.g.:
def make_closure(v):
return lambda: print_index(v)
func = []
for value in range(5):
func.append(make_closure(value))
Now this outputs:
In []:
for a in func:
a()
Out[]:
0
1
2
3
4
As #AChampion said, Python calculates the value of value when it needs to and not while executing the loop.
However for me it seemed easier to do the following fix.
func = []
for value in range(5):
func.append(lambda v = value: print_index(v))
def print_index(index_to_print):
print(index_to_print)
for a in func:
a()
This is setting the default argument in the function which gets calculated during the loop execution. The code above is the same as saying :
func = []
for value in range(5):
def anonymous_function(v=value):
print_index(v)
func.append(anonymous_function)
def print_index(index_to_print):
print(index_to_print)
for a in func:
a()

Static variable in Python?

In C++ we have static keyword which in loops is something like this:
for(int x=0; x<10; x++)
{
for(int y=0; y<10; y++)
{
static int number_of_times = 0;
number_of_times++;
}
}
static here makes number_of_times initialized once. How can I do same thing in python 3.x?
EDIT: Since most of the people got confused I would like to point out that the code I gave is just example of static usage in C++. My real problem is that I want to initialize only ONE time variable in function since I dont want it to be global(blah!) or default parameter..
Assuming what you want is "a variable that is initialised only once on first function call", there's no such thing in Python syntax. But there are ways to get a similar result:
1 - Use a global. Note that in Python, 'global' really means 'global to the module', not 'global to the process':
_number_of_times = 0
def yourfunc(x, y):
global _number_of_times
for i in range(x):
for j in range(y):
_number_of_times += 1
2 - Wrap you code in a class and use a class attribute (ie: an attribute that is shared by all instances). :
class Foo(object):
_number_of_times = 0
#classmethod
def yourfunc(cls, x, y):
for i in range(x):
for j in range(y):
cls._number_of_times += 1
Note that I used a classmethod since this code snippet doesn't need anything from an instance
3 - Wrap you code in a class, use an instance attribute and provide a shortcut for the method:
class Foo(object):
def __init__(self):
self._number_of_times = 0
def yourfunc(self, x, y):
for i in range(x):
for j in range(y):
self._number_of_times += 1
yourfunc = Foo().yourfunc
4 - Write a callable class and provide a shortcut:
class Foo(object):
def __init__(self):
self._number_of_times = 0
def __call__(self, x, y):
for i in range(x):
for j in range(y):
self._number_of_times += 1
yourfunc = Foo()
4 bis - use a class attribute and a metaclass
class Callable(type):
def __call__(self, *args, **kw):
return self._call(*args, **kw)
class yourfunc(object):
__metaclass__ = Callable
_numer_of_times = 0
#classmethod
def _call(cls, x, y):
for i in range(x):
for j in range(y):
cls._number_of_time += 1
5 - Make a "creative" use of function's default arguments being instantiated only once on module import:
def yourfunc(x, y, _hack=[0]):
for i in range(x):
for j in range(y):
_hack[0] += 1
There are still some other possible solutions / hacks, but I think you get the big picture now.
EDIT: given the op's clarifications, ie "Lets say you have a recursive function with default parameter but if someone actually tries to give one more argument to your function it could be catastrophic", it looks like what the OP really wants is something like:
# private recursive function using a default param the caller shouldn't set
def _walk(tree, callback, level=0):
callback(tree, level)
for child in tree.children:
_walk(child, callback, level+1):
# public wrapper without the default param
def walk(tree, callback):
_walk(tree, callback)
Which, BTW, prove we really had Yet Another XY Problem...
You can create a closure with nonlocal to make them editable (python 3.x only). Here's an example of a recursive function to calculate the length of a list.
def recursive_len(l):
res = 0
def inner(l2):
nonlocal res
if l2:
res += 1
inner(l2[1:])
inner(l)
return res
Or, you can assign an attribute to the function itself. Using the trick from here:
def fn(self):
self.number_of_times += 1
fn.func_defaults = (fn,)
fn.number_of_times = 0
fn()
fn()
fn()
print (fn.number_of_times)
Python doesn't have static variables by design. For your example, and use within loop blocks etc. in general, you just use a variable in an outer scope; if that makes it too long-lived, it might be time to consider breaking up that function into smaller ones.
For a variable that continues to exist between calls to a function, that's just reimplementing the basic idea of an object and a method on that object, so you should make one of those instead.
The another function-based way of doing this in python is:
def f(arg, static_var=[0]):
static_var[0] += arg
As the static_var object is initialised at the function definition, and then reused for all the calls, it will act like a static variable. Note that you can't just use an int, as they are immutable.
>>> def f(arg, static_var=[0]):
... static_var[0] += arg
... print(static_var[0])
...
>>> f(1)
1
>>> f(2)
3
>>> f(3)
6
You can also use the global keyword:
def main(args):
for i in xrange(10):
print i
global tmp
tmp = i
But be careful... I most cases it will add more issues than it solves.
Use defaultdict:
from collections import defaultdict
static = defaultdict(lambda: 0)
def myfunc():
for x in range(10):
for y in range(10):
static['number_of_times'] += 1

Making an addition accumulator in python [duplicate]

This question already has answers here:
How to find the cumulative sum of numbers in a list?
(25 answers)
Closed 8 years ago.
What is desired is to
def accumulator():
def addition():
something here
and others
return addition
where
A = accumulator()
A(10)
10
A(10)
20
using Message Passing such that every accumulator() will have the overall sum at the end.
I have no idea how to start. . . I would be really appreciative if you could help!
Thanks
Just another variant, with closures.
def accumulator():
sum = 0
def addition(n):
nonlocal sum
sum += n
return sum
return addition
You could define a coroutine, as Aशwini चhaudhary suggested:
def coroutine(func):
"""
http://www.python.org/dev/peps/pep-0342/
http://www.dabeaz.com/coroutines/index.html
"""
def wrapper(*args, **kw):
gen = func(*args, **kw)
gen.send(None)
return gen
return wrapper
#coroutine
def accumulator():
val = 0
while True:
val += (yield val)
which can be used like this:
A = accumulator().send
print(A(10))
# 10
print(A(10))
# 20
print(A(10))
# 30
Not sure if this is what you are asking, but you can get the desired effect by updating a class variable.
class accumulator(object):
summation = 0
def __call__(self,val):
accumulator.summation+=val
print accumulator.summation
A = accumulator()
A(10)
A(10)
B = accumulator()
B(10)
B(100)
This will give:
10
20
30
130
and every instance will have the correct value in the summation attribute.
If you want to keep the summations of A and B separate:
class accumulator(object):
def __init__(self):
self.summation = 0
def __call__(self,val):
self.summation+=val
print self.summation

Is it possible to make Python functions behave like instances?

I understand that functions can have attributes. So I can do the following:
def myfunc():
myfunc.attribute += 1
print(myfunc.attribute)
myfunc.attribute = 1
Is it possible by any means to make such a function behave as if it were an instance? For example, I'd like to be able to do something like this:
x = clever_wrapper(myfunc)
y = clever_wrapper(myfunc)
x.attribute = 5
y.attribute = 9
x() # I want this to print 6 (from the 5 plus increment)
y() # I want this to print 10 (from the 9 plus increment)
As it stands, there is only one "instance" of the function, so attribute only exists once. Modifying it by either x or y changes the same value. I'd like each of them to have their own attribute. Is that possible to do at all? If so, can you provide a simple, functional example?
It is important that I be able to access attribute from inside of the function but have the value of attribute be different depending on which "instance" of the function is called. Essentially, I'd like to use attribute as if it were another parameter to the function (so that it could change the behavior of the function) but not pass it in. (Suppose that the signature of the function were fixed so that I cannot change the parameter list.) But I need to be able to set the different values for attribute and then call the functions in sequence. I hope that makes sense.
The main answers seem to be saying to do something like this:
class wrapper(object):
def __init__(self, target):
self.target = target
def __call__(self, *args, **kwargs):
return self.target(*args, **kwargs)
def test(a):
return a + test.attribute
x = wrapper(test)
y = wrapper(test)
x.attribute = 2
y.attribute = 3
print(x.attribute)
print(y.attribute)
print(x(3))
print(y(7))
But that doesn't work. Maybe I've done it incorrectly, but it says that test does not have attribute. (I'm assuming that it's because wrapper actually has the attribute.)
The reason I need this is because I have a library that expects a function with a particular signature. It's possible to put those functions into a pipeline of sorts so that they're called in order. I'd like to pass it multiple versions of the same function but change their behavior based on an attribute's value. So I'd like to be able to add x and y to the pipeline, as opposed to having to implement a test1 function and a test2 function that both do almost exactly the same thing (except for the value of the attribute).
You can make a class with a __call__ method which would achieve a similar thing.
Edit for clarity: Instead of making myfunc a function, make it a callable class. It walks like a function and it quacks like a function, but it can have members like a class.
A nicer way:
def funfactory( attribute ):
def func( *args, **kwargs ):
# stuff
print( attribute )
# more stuff
return func
x = funfactory( 1 )
y = funfactory( 2 )
x( ) # 1
y( ) # 2
This works because the functions are closures, so they will grab all local variables in their scope; this causes a copy of attribute to be passed around with the function.
class Callable(object):
def __init__(self, x):
self.x = x
def __call__(self):
self.x += 1
print self.x
>> c1 = Callable(5)
>> c2 = Callable(20)
>> c1()
6
>> c1()
7
>> c2()
21
>> c2()
22
A generator might be an alternate solution here:
def incgen(init):
while True:
init += 1
print init
yield
x = incgen(5)
y = incgen(9)
x.next() # prints 6
y.next() # prints 10
y.next() # prints 11
x.next() # prints 7
You can't dig back in to the generator and manipulate the data though.
#!/usr/bin/env python
# encoding: utf-8
class Callable(object):
attribute = 0
def __call__(self, *args, **kwargs):
return self.attribute
def main():
c = Callable()
c.attribute += 1
print c()
if __name__ == '__main__':
main()

Categories

Resources