Replacing global vars - python

Below, I came up with artificially passing the reference var n2 from f2() to g2(x) instead of the global var n in f() and the nested g(). Any other better ways to replace global vars in this case?
from random import randint
# global value var
def f():
global n
n=0
def g():
global n
if randint(1,100)>50: n+=1
for _ in range(100): g()
print(n)
# local reference var
def f2():
n2=[0]
for _ in range(100): g2(n2)
print(n2[0])
def g2(x):
if randint(1,100)>50: x[0]+=1

Short answer: You are trying to pass by reference an immutable value (integer) and want to update it. Wrapping that in a tiny class, or list, or dict like you're doing is the way to go. But there are other ways if you are able to slightly modify your code.
Longer answer: (Note: This might not be a direct answer to your question.)
I understand this is an artificial example. But think about your real problem --Does g2() need to know that there is a variable that is supposed to update as a part of its invocation? Is there a way that the responsibility of updating a variable belongs to that which defines it? How about f2() is the one that defines the variables and also updates it? That way you can limit all the changes to that variable to a very small perimeter (f2()).
My approach in that case would be something like:
def f2():
n2 = 0
for _ in range(100):
n2 += g2()
print(n2)
def g2():
return 1 if randint(1,100)>50 else 0

From working with functional languages, and from trying to write reproducible tests, I've generally tried to adopt a rule that a function should declare all of its inputs as parameters, and produce all of its outputs as return values: to the maximum extent possible a function should never have side effects. Using the global keyword probably indicates you're breaking this rule.
For example, your "g" function takes the current state variable and either increments it or doesn't. You don't need a global for that (and you don't need it to be a nested function either):
from random import randint
def g(n):
"""Returns either n or n+1, with 50% probability."""
if randint(1,100)>50:
return n+1
else:
return n
Then the iterating function can call that a bunch of times:
def f():
"""Produces a number between 0 and 100 with $DISTRIBUTION."""
n = 0
for _ in range(100):
n = g(n)
return n
And finally at the top level:
if __name__ == '__main__':
print(f())
Since we're never totally sure about our code, we can write some tests.
def test_f():
n = f()
assert n >= 0 and n < 100
def test_g():
n = g(0)
assert n == 0 or n == 1
def test_g_dist():
count = 100
ns = [g(0) for _ in range(count)]
assert(all(n == 0 or n == 1) for n in ns)
zeros = len([n for n in ns if n == 0])
ones = len([n for n in ns if n == 1])
assert zeros + ones == count
# won't always pass; probability of failure left as an exercise
assert zeros > 0.45 * count and zeros < 0.55 * count
Notice that I can call f() and g(n) as many times as I want and they'll never interfere with anything else. Running my own unit tests at startup time would be a little unusual, but I'm free to if that's what I want to do.

Related

Iteration count in recursive function

I am writing a recursive function to make permutations of digits from 0 to n. The program will return the th permutation that is obtained. It all works well but I had to use the cheap trick of defining count as a list, that is count=[0]. In this way I am using the properties of lists in order to properly update the variable count[0] at each iteration.
Ideally, what I would like to do is to define count as an integer number instead. However, this does not work because count is then updated only locally, within the scope of the function at the time it is called.
What is the proper way to count the iterations in a recursive function like this?
Below I show the code. It works, but I hate the way I am using count here.
import numpy as np
N=10
available=np.ones(N)
def permutations(array, count=[0], n=N, start=0, end=N, th=100):
if count[0]<th:
for i in range(n):
if available[i]:
array[start]=i
if end-start>1:
available[i]=0
permutations(array, count, n, start+1, end)
available[i]=1
else:
count[0]+=1
break
if count[0]==th:
a=''.join(str(i) for i in array)
return a
def main():
array=[0 for _ in range(N)]
count=[0]
print(permutations(array, count, N, start=0, end=N))
if __name__=="__main__":
main()
Not necessarily ideal but to answer the question, one could use a global variable as follows:
import numpy as np
N = 10
available = np.ones(N)
count = 0
def permutations(array, n=N, start=0, end=N, th=100):
global count
if count < th:
for i in range(n):
if available[i]:
array[start] = i
if end-start > 1:
available[i] = 0
permutations(array, n, start+1, end)
available[i] = 1
else:
count += 1
break
if count == th:
return ''.join(str(i) for i in array)
def main():
array = [0 for _ in range(N)]
print(permutations(array, N, start=0, end=N))
print(f'{count=}')
if __name__ == "__main__":
main()
Output:
0123495786
count=100
Different ways to update a variable from other scopes... and each with its own advantages and disadvantages (performance, access to the variable, ...):
with global approach (as Pingu did),
with nonlocal
and with function's attribute.
The example under consideration, the factorial function, is merely illustrative but it can be easily readapted to your case.
def fact_global(n):
global counter
counter += 1
if n == 1:
return 1
return n*fact_global(n-1)
def fact_nonlocal(n):
counter = 0
def __fact(n):
nonlocal counter
counter += 1
if n == 1:
return 1
return n*__fact(n-1)
return __fact(n)
def fact_attr(n):
fact_attr.counter = 0
def __fact(n):
fact_attr.counter += 1
if n == 1:
return 1
return n*__fact(n-1)
return __fact(n)
n = 10
# case: global
counter = 0
fact_global(n)
print('global', counter)
# case: nonlocal
fact_nonlocal(n)
import inspect
fr = inspect.currentframe()
print('nonlocal', fr.f_locals['counter']) # not recommended, just for curiosity!
# case: function's attribute
fact_attr(n)
print('attr', fact_attr.counter)
Retrieving the value of the variable under investigation is quite straightforward with a global-setting and with function's attribute but not trivial (and not recommended) with nonlocal (inspect is more a debugging tool).
Here a partial result of the workbench:
n=860
fact_nonlocal(n): 2.60644 ± 0.00586
fact_global(n): 2.74698 ± 0.02283
fact_attr(n): 3.01219 ± 0.00539
The results are of the same order of magnitude (due to limitations of the host only tested with a maximum of n=860 so not reliable for an asymptotic conclusion), in this case it seems that it doesn't really matter which one you choose but it is more important to focus on how you are going to access to the variable later in the program.

How to make a function memorize 2 variable function values?

I want to improve the performance of a recursive calculation of values
n = 100
def T(n,k):
q = 0
if n == 0 and k == 0:
return(1)
q = 1
if k>n or n<0:
return(0)
q = 1
if q != 1:
return(T(n-1,k-1)+n*T(n-1,k))
for i in range(n):
for n in range(i+1):
print(T(i,n))
print("*********")
However, I've only found ways to do this with functions that only take 1 argument, like this:
def mem(f):
memory = {}
def inner_function(x):
if x not in memory:
memory[x] = f(x)
return memory[x]
else:
return memory[x]
return inner_function
#mem
def fibonacci(n):
if n == 1 or n == 0:
return 1
else:
return fibonacci(n-1) + fibonacci(n-2)
I was thinking of doing a 2d array, but I don't know yet (assuming it's possible) how the idea of doing so, with lists of lists, would help.
You can use functools.lru_cache for this
from functools import lru_cache
#lru_cache(maxsize=32)
def T(n,k):
q = 0
if n == 0 and k == 0:
return(1)
q = 1
if k>n or n<0:
return(0)
q = 1
if q != 1:
return(T(n-1,k-1)+n*T(n-1,k))
You can use this decorator to memoize function calls, with this function in particular this will save up to the maxsize most recent calls.
Note in this particular case, the vast majority of the time is actually spent writing to console because of your print statements. If you remove this (but still leave your T(i,n) invocation) your code will complete almost instantaneously.
You can easily extend your mem decorator to work with variable *args parameters, looking them up in the memory. This would work with **kwargs, too, you'd have to convert them to a hashable type though, e.g. frozenset of tuples. And of course all parameters must be hashable for this.
def mem(f):
memory = {}
def inner_function(*args):
if args not in memory:
memory[args] = f(*args)
return memory[args]
return inner_function
Tested with your T function, works fine. In practice, however, you might still want to use functools.lru_cache.

How to create a recursive function, that generates multiple for loops and counts the total times

I tried to count the total number of calling times via a recursive function that generates n for-loops, but the variable seems to never change its value.
a=0
def recursivelooping(times,volumes):
if times==0:
a+=1
else:
for i in range(volumes):
return recursivelooping(times-1,i)
The result should look more like the variable a below, but instead I always got a zero.
def multiforloop(volumes):
a=0
for i in range(volumes):
for j in range(i):
for k in range(j):
a+=1
print(a)
If I understand correctly having a closure maybe the shortest solution:
def recursivelooping():
a = 0
def f(times, volumes):
nonlocal a
if volumes == 0:
return a
# Inner loop
for t in range(times):
for j in range(volumes):
a+=1
# Outer loop
return f(times, volumes-1)
return f
def multiforloop(times, volumes):
a=0
for i in range(volumes+1):
for t in range(times):
for j in range(i):
a+=1
return a
print(recursivelooping()(1, 10))
print(multiforloop(1, 10))
This will print 55 for both (as in n*(n+1)/2). A closure simply is a function (here f) accompanied by an environment (some context that maps names to variables, here a alone). That mean that a is accessible from within f as if it was a local variable, but it's not exactly a local variable it's a free variable
When you write a += 1 the normal behavior is to look for a local variable named a, instead here a is a free variable, that's why we need to add the nonlocal keyword to specify this.

Python Global Variables for Recursive Function

In my math class, we have been told to write a program in python. In one of the parts, I want to test the convergence of a series. While writing the program, I realized I was fundamentally misunderstanding something about how python handles global variables. Take this code:
def main():
global n
n = 1
def check():
a = 10
if n > a: print(n)
else: n += 1
check()
main()
This code fails because it says that n has not been defined yet. However, I can't define n inside of the check() function since that would just reset n to one upon every iteration! Is there some workaround for this problem?
As already stated in the comments, n isn't in the global scope yet, since it's inside the nested function check. You will need to add global n to the check's scope, to access the global n value from the nested function:
def main():
global n
n = 1
def check():
global n
a = 10
if n > a: print(n)
else: n += 1
check()
main()
#PedrovonHertwig also pointed out you don't need global n in main (which is the case in your current context, ignore this if you want to use n elsewhere in the top-level scope) and thatn is perfectly fine staying in main's local scope. You can then replace the global keyword inside check to nonlocal n, telling python to use the n that is not in the local scope nor the global scope, but main's scope.

Calculate nth term of Fibonacci sequence in Python

The following code is to calculate nth term og fibonacci sequence in python using matrix exponentiation for various test cases t.But the program gives absurd output.Please tell me where i am wrong.when i ran the code in C++ it runs perfectly.
class matrix:
def __init__(self):
self.a=self.b=self.c=1
self.d=0
def mul(self,e,f):
ret = matrix()
ret.a=(e.a*f.a)+(e.b+f.c)
ret.b=(e.a*f.b)+(e.b+f.d)
ret.c=(e.c*f.a)+(e.d+f.c)
ret.d=(e.c*f.b)+(e.d+f.d)
return ret
def exp(self,a,p):
if(p==0):
temp=matrix()
temp.a=temp.b=temp.c=temp.d=1
return temp
if(p==1):
return a
if(p%2==0):
return self.exp(self.mul(a,a),p/2)
else:
return self.mul(a,self.exp(self.mul(a,a),(p-1)/2))
def fib(self,n):
if (n==0):
return 0
if (n==1):
return 1
s=matrix()
s=self.exp(s,n)
return s.d
t=int(raw_input())
while(t>0):
v=matrix()
n=int(raw_input())
print v.fib(n)
t=t-1
The problem lies in your __init__ function. In python the so-called variables are just 'tags' to data in the memory. To compare with C/C++, these can be thought of as pointers. when you assign self.a = self.b = self.c, you are basically assigning three different names to the same data in the memory. Any change you make in a will be reflected back in b and c and so on.
For your problem where you need three separate variables, one way to change the __init__ function is like:
self.a, self.b, self.c = 1, 1, 1
or you can use copy. copy() tells python to assign a new memory location and then assign the tag on the right hand side to that location. For more read the official documentation on this http://docs.python.org/2/library/copy.html. You can also read a short walk-through on this in Python Tutorial: Shallow and Deep-copy
There are several issues, in order of importance:
1) Your multiplication is wrong. Note the multiplications at the right where you have sums):
def mul(self,e,f):
ret = matrix()
ret.a=(e.a*f.a)+(e.b*f.c)
ret.b=(e.a*f.b)+(e.b*f.d)
ret.c=(e.c*f.a)+(e.d*f.c)
ret.d=(e.c*f.b)+(e.d*f.d)
return ret
2) In the last line, you do return s.d but you should return s.b or s.c or you will get one less fibonacci.
3) The line temp.a=temp.b=temp.c=temp.d=1 is not necessary because the constructor does the work. Besides it is wrong, because d should be 0.
4) Why are mul and exp class functions if they don't use self. It does no harm but they should be #staticmethod
5) Again, it does no harm but your second recursive call is unnecessarily complex. Just write:
return matrix.mul(a,matrix.exp(a, p-1))
I'm not sure if it is required for you to use matrix exponentiation for this problem. Unfortunately, I do not know much about Python classes quite yet. However, the following code does what the question heading wants: to find the n-th Fibonacci number. Below I describe this as F_n. Note the initial conditions for low values of n.
def fibN( n ):
"""
fibonacci: int -> int
Returns F_n.
Note: F_1 = 0, F_2 = 1, F_3 = 1, F_4 = 2
"""
n = abs( int( n ))
if n == 0:
fib = 0
elif n == 1:
fib = 1
else:
counter = 2
f0 = 0
f1 = 1
fib = f0 + f1
while counter <= n:
fib = f0 + f1
f0 = f1
f1 = fib
counter += 1
return fib

Categories