Access a method's local variables in Python - python

Was going through a Python Tutorial and have asked a related question here(Code from my previous example and thanks to #emmanuel)
Code:
import math, time
class PrimeFactor:
def __init__(self):
pass
def isprime(self,number):
start=time.clock()
fnum = [1,]
print "Reticulating Splines..."
last = int(math.ceil(math.sqrt(number)))
for p in range(2, last + 1):
if (number % p) == 0:
fnum.append(p)
fnum.append(number / p)
# Remove duplicates, sort list
fnum = list(set(fnum))
fnum.sort()
end=time.clock()
if len(fnum) > 1:
return number, "is not a prime because of these factors", fnum ,"Time taken", end-start
else:
return True, "Time taken", end-start
print "Prime or factor calculator v3 using sqrt(n)"
print #
num =int(raw_input("Enter number: "))
eg=PrimeFactor()
print eg.isprime(num)
From this code, I tried to get the variable fnum which is local to the function(method) isprime, which contains the factor list. Accessing it by calling
print eg.isprime(num).fnum
gave me a error
AttributeError: 'tuple' object has no attribute 'fnum'
I guess I cannot call a local variable that way.
Plus the code is not reusable. So I decided to do a rewrite, to make it more modular.
Rewritten Code:
import math
class PrimeFactor:
def __init__(self):
pass
def isPrime(self,number):
fnum = [1,]
last = int(math.ceil(math.sqrt(number)))
for p in range(2, last + 1):
if (number % p) == 0:
return False
else:
return True
def getFactors(self,number):
fnum = [1,]
last = int(math.ceil(math.sqrt(number)))
for p in range(2, last + 1):
if (number % p) == 0:
fnum.append(p)
fnum.append(number / p)
# Remove duplicates, sort list
fnum = list(set(fnum))
fnum.sort()
if len(fnum) > 1:
return fnum
else:
return None
num =int(raw_input("Enter number: "))
eg=PrimeFactor()
if eg.isPrime(num):
print num, "is a Prime Number"
else:
print num, "is not a prime number"
print "Factors", eg.getFactors(num)
I had to forgo time calculation. I can calculate time while using an instance.
Question:
Can I access the local variable fnum in my previous example? If yes, How?(I guess no). If no, then is the rewritten code good enough or am I doing it wrong?

The problem with your modification is that you're duplicating the calculus. I understand you just want to get the calculated factors, which just involves creating fnum as an attribute:
import math, time
class PrimeFactor:
def __init__(self):
self.fnum = [1,]
self.elapsedTime = 0
def getElapsedTime(self):
return self.elapsedTime
def getFactors(self):
return self.fnum
def isprime(self,number):
start=time.clock()
self.fnum = [1,]
last = int(math.ceil(math.sqrt(number)))
for p in range(2, last + 1):
if (number % p) == 0:
self.fnum.append(p)
self.fnum.append(number / p)
# Remove duplicates, sort list
self.fnum = list(set(self.fnum))
self.fnum.sort()
end=time.clock()
self.elapsedTime = end-start
return (not len(self.fnum) > 1 )
num =int(raw_input("Enter number: "))
eg=PrimeFactor()
if eg.isprime(num):
print num, "is a Prime Number", eg.isprime(num)
else:
print num, "is not a prime number"
print "Factors", eg.getFactors()
print eg.getElapsedTime()
You could even develop the code a little bit more and take advantage of the previous calculated factors, thus using dynamic programming.
Hope this helps.

You need:
print eg.isprime(num)[2]
(your method is returning a tuple, and you need the third member, that's all)

There is no way to access the local variables of a function from outside that function. You need to explicitly make them available to callers, either by returning them, or by writing their values into a container available to the caller.

Related

Why am I getting nonetype when calling a function within a function in Python?

I'm tying to do a Collatz sequence with python code. I'm supposed to make a function that, given n, calculates the next number in the sequence. I want the next function "write" to print each number within the sequence.
My code so far:
def collatz(n):
while n != 1:
if n % 2 == 0:
n = n/2
return write(n)
else:
n = 3*n+1
return write(n)
def write(n):
print(n)
print(collatz(n))
write(6)
It gives me the right sequence, which should be 6, 3,10,5,16,8,4,2,1, but also gives me 9 "nones".
I'm new to programming, it should probably be something easy, but I can't figure out what.
write() is a function that executes two print() statements, and then implicitly returns None (since there are no return statements in the function).
You can simplify the code by using print() directly in collatz(), and eliminating the mutual recursion:
def collatz(n):
while n != 1:
if n % 2 == 0:
n = n//2
print(n)
else:
n = 3*n+1
print(n)
collatz(6)
#Here this will help you understand.
#When n becomes 1, the while loop is not executed,
#collatz does not return write(n)... thus returns None
def collatz(n):
while n != 1:
if n % 2 == 0:
n = n/2
return write(n)
else:
n = 3*n+1
return write(n)
#return None.... there is no return type, so this is implied
def write(n):
print(n)
result = collatz(n)
if result != None:
print(collatz(n))

Next Prime Number in Python

I'm a beginner in Python and I'm practicing to code this problem I saw. Next prime is needed, but there are limitations on the input. I have searched for similar questions, but my code is still not working. Hope you can help. Thank you!
The problem I get is when I enter 32, the results show 33 when the next prime is 37...
Here's my code so far.
num = int(input("Enter a positive number:"))
import math
def nextprime(n):
if n < 0:
raise ValueError
for next in range(n + 1, n +200):
if next > 1:
for i in range(2, next):
if (next % i) == 0:
break
else:
return next
In your code when you arrive to a number that reminder is not zero you return that number. You need a flag for every number this flag is True if can be divide flag convert to False for the first number that flag not convert to false return that number like below.
Don't use next because this is a builtin function.
Try this: (I don't improve your code)
def nextprime(n):
if n < 0:
raise ValueError
for i in range(n + 1, n +200):
if i > 1:
pr = True
for j in range(2, i):
if (i % j) == 0:
pr = False
break
if pr:
return i
return 'not found'
You can also try this code, write function to check that a number is prime or not like def is_prime then for number of larger that you input num find min number next. (this answer from this thread.)
def is_prime(x):
return all(x % i for i in range(2, x))
def next_prime(x):
return min([a for a in range(x+1, 2*x) if is_prime(a)])
print(next_prime(32))
You can also use sympy like below: (this answer from this thread.)
from sympy import *
nextprime(32)
def next_prime(n):
while True:
n=n+1
for i in range (2,int(n/2)):
if n%i==0:
break
else:
return n
print(next_prime(67))
Few off-topic tips:
as user1740577 mentioned, don't use next as a variable name
refrain from using eval when possible, it's okay here, but in real project this will lead to big no-no.
Place imports at the very top of your script
Consider using variable names i and j only for iterations.
For duplicate except blocks use (Error, Error)
As for solution to your problem, with some adjustments, if you don't mind
def next_prime(n: int) -> int:
if n < 0:
raise ValueError('Negative numbers can not be primes')
# Base case
if n <= 1:
return 2
# For i as every odd number between n + 1 and n + 200
for i in range(n + 1 + (n % 2), n + 200, 2):
# For every odd number from 3 to i (3 because we covered base case)
for j in range(3, i, 2):
# If remained is equals to 0
if not i % j:
# break current loop
break
# If loop j didn't break [nobreak: ]
else:
return i
raise RuntimeError('Failed to compute next prime number :c')
def main():
while True:
try:
num = int(input('Enter positive number: '))
print(f'Next prime is: {next_prime(num)}')
break
except ValueError:
print('Please enter a positive integer!')
if __name__ == '__main__':
main()
Made some speed improvements to the code from #rajendra-kumbar:
#!/usr/bin/env python
import sys
import time
import math
def next_prime(number):
if number < 0:
raise ValueError('Negative numbers can not be primes')
# Base case
if number <= 1:
return 2
# if even go back 1
if number % 2 == 0:
number -= 1
while True:
# only odds
number += 2
#only need to check up to and including the sqrt
max_check = int(math.sqrt(number))+2
# don't need to check even numbers
for divider in range(3, max_check, 2):
# if 'divider' divides 'number', then 'number' is not prime
if number % divider == 0:
break
# if the for loop didn't break, then 'number' is prime
else:
return number
if __name__ == '__main__':
number = int(sys.argv[1].strip())
t0 = time.time()
print('{0:d} is the next prime from {1:d}'.format(next_prime(number), number))
run_time = time.time() - t0
print('run_time = {0:.8f}'.format(run_time))
it is about twice as fast
You can try something like simple:
def is_prime(number:int):
check = 0
for i in range(2,number):
if number % i == 0:
check += 1
if check == 0:
return True
else:
return False
def next_prime(value):
check = value + 1
while is_prime(check) is False:
check += 1
return check
value = int(input("Insert the number: "))
print(next_prime(value))

Why do I get a different answer using math.fmod vs mod operator (positive integers only)

Trying to find the smallest positive integer n such that n! divided by the sum of the digits of n! has a remainder.
Two people approached the problem in similar ways but got different answers. I've read that % and fmod behave differently with negative numbers but there are no negative numbers involved here. Large number calculators online seem to show that the solution arrived at through % is correct.
Using mod operator:
import math
n=1
def func():
global n
intn=[]
#for loop creates a list of integers of the digits of n!
for digit in str(math.factorial(n)):
intn.append(int(digit))
denominator=sum(intn)
#if n!/denominator has a remainder, print n; the program is over. Otherwise, increase n and try again.
if (math.factorial(n))%denominator:
print(n)
else:
n+=1
func()
func()
Changing if (math.factorial(n))%(denominator): to if int((math.factorial(n)))%int(denominator): changed the result so that it is the same as when using .fmod, but again, I don't think that is the correct answer.
Using math.fmod:
import math
# curNum is the variable I'm using to track the integer whose value I'm testing
curNum = 1
# conditionMet is a boolean that will be used to break the while loop of the test
conditionMet = False
while conditionMet == False:
sumDigits = 0
curFactorial = math.factorial(curNum)
curFactorialAsString = str(curFactorial)
#sumDigits = sum(int(curNumAsString))
for curDigit in curFactorialAsString:
sumDigits = sumDigits + int(curDigit)
if math.fmod(curFactorial,sumDigits) != 0:
print("curNum: " + str(curNum) + "curFactorial: " + str(curFactorial) + "sumDigits: " + str(sumDigits))
conditionMet = true
else:
curNum = curNum + 1

Hofstadter equation related code in python

There was this question in a university assignment related to Hofstadter sequence. It basically say that it is a integer sequence blah blah and there are two values for a given index n. A male value [M(n)] and a female value [F(n)].
They are defined as:
M(0)=0
F(0)=1
M(n)=n-F(M(n-1))
F(n)=n-M(F(n-1))
And we were asked to write a program in python to find the Male and Female values of the sequence of a given integer.
So the code I wrote was:
def female(num):
if num == 0:
return 1
elif num >0:
return num - male(female(num-1))
def male(num):
if num==0:
return 0
elif num >0:
return num - female(male(num-1))
And when executed with a command like
print male(5)
It works without a fuss. But when I try to find the value of n = 300, the program gives no output.
So I added a print method inside one of the functions to find out what happens to the num value
[ elif num>0:
print num ...]
And it shows that the num value is decreasing until 1 and keeps hopping between 1 and 2 at times reaching values like 6.
I can’t figure out why it happens. Any insight would be nice. Also what should I do to find the values relevant to bigger integers. Thanks in advance. Cheers.
The code is theoretically fine. What you underestimate is the complexity of the computation. Formula
M(n)=n-F(M(n-1))
actually means
tmp = M(n-1)
M(n) = n - F(tmp)
So if you represent this calculation as a tree of necessary calls, you might see that its a binary tree and you should go through all its nodes to calculate the results. Given that M(n) and F(n) are about n/2 I'd estimate the total number of the nodes to be of order 2^(n/2) which becomes a huge number once you put n = 300 there. So the code works but it just will take a very very long time to finish.
The one way to work this around is to employ caching in a form of memoization. A code like this:
femCache = dict()
def female(num):
#print("female " + str(num))
global femCache
if num in femCache:
return femCache[num];
else:
res = 1 # value for num = 0
if num > 0:
res = num - male(female(num-1))
femCache[num] = res
return res
maleCache = dict()
def male(num):
#print("male " + str(num))
global maleCache
if num in maleCache:
return maleCache[num];
else:
res = 0 # value for num = 0
if num > 0:
res = num - female(male(num-1))
maleCache[num] = res
return res
print(male(300))
should be able to compute male(300) in no time.

How can I reimplement this recursive function?

So I'm trying to solve a puzzle and I came across this code. I can't figure out what the output would be and any attempts to run it result in a "reached the maximum amount of recursion" error. Sorry in advance for the goofy variable names.
How can I modify this to achieve the same output, without the recursion error? The initial numbers I'm passing are 13379446(arg 1) and 5(arg 2).
import sys
num1 = int(sys.argv[1])
num2 = int(sys.argv[2])
def doot(num1, num2):
print num1
print num2
doritos = 0
if num2 > num1:
pass
else:
if num2 == 0:
print "if"
doritos = 1
else:
if num2 == num1:
doritos = 1
else:
wew = doot(num1 - 1, num2 - 1)
doritos = wew
wew = doot(num1 -1, num2)
doritos = doritos + wew
print doritos
Let me get you started. I'm assuming that doritos is supposed to be the return value, that is, that the code should say return dortitos instead of print doritos. Also, I'm completely ignoring the line print if.
Now what do we know? Looking at the code we see that
doot(x,y) = 0 if y > x
doot(x,0) = 1
doot(x,x) = 1 and
doot(x,y) = doot(x-1,y-1) + doot(x-1,y) otherwise
So we want to figure out the value of doot(x+h,x) where h > 0. Start with the simplest case, h=1. We already know that doot(1,0)=1, so
doot(2,1) = doot(1,0)+doot(1,1) = 1+1 = 2
doot(3,2) = doot(2,1)+doot(2,2) = 2+1 = 3
and now it's easy to guess that
doot(x+1,x)=x+1 for all x>= 0.
t's also easy to prove this by induction, if you're so inclined.
So now, work out some examples when h=2, and figure out the formula for doot(x+2,x). Then figure out the formula for doot(x+3,x) and so on, until you're ready to guess the the formula for doot(x+h,x)
Okay, I overhauled your code to use a dictionary (values) and a queue.
import sys
num1 = int(raw_input("num1: "))
num2 = int(raw_input("num2: "))
def doot(num1, num2):
print num1
print num2
doritos = 0
n1 = num1
n2 = num2
values = {}
queue = [(num1,num2)]
while queue:
num1,num2 = queue.pop()
#print queue
#print values
if (num1,num2) not in values:
if num1 >= num2:
if num2 == 0:
#print "if"
#doritos = 1
values[(num1,num2)] = 1
else:
if num2 == num1:
#doritos = 1
values[(num1,num2)] = 1
else:
#wew = doot(num1 - 1, num2 - 1)
#doritos = wew
#wew = doot(num1 -1, num2)
#doritos = doritos + wew
if (num1-1,num2-1) in values and (num1-1,num2) in values:
values[(num1,num2)] = values[(num1-1,num2-1)] + values[(num1-1,num2)]
else:
queue.append((num1,num2))
if (num1-1,num2) not in values:
queue.append((num1-1,num2))
if (num1-1,num2-1) not in values:
queue.append((num1-1,num2-1))
#print values
doritos = values[(n1,n2)]
print doritos
return doritos
doot(num1,num2)
In essence, I use the queue to keep track of the sums I don't have yet. If both of the descendants of (num1,num2) are in the dictionary, then I put it in the dictionary with the sum of their values. Otherwise, I put either or both descendant that's not in the dictionary on the queue along with itself. The other times that I don't put (num1,num2) back on the queue are when num1 == num2 or num2 == 0, in which cases I put them in the dictionary with value 1. At the end, I return the value in the dictionary that corresponds to the original inputted numbers.
Now, a word of caution: this code is horribly inefficient. I just had to reboot my computer because I tried the inputs you gave in the question, and it gobbled up all of the available RAM. So, you should instead consider what exactly is being done with the recursion, and figure out how to work forwards from the base cases instead of backwards from the input. That task, I will leave to you.
Your code is a recursive implementation of a combination function, which calculates the number of combinations of k distinct elements that can be drawn from a set of n elements.
A tidied up version of your function would look like this (note I have removed all the print statements and ensured that the function returns something - otherwise it won't work at all).
def doot(n, k):
if n < k:
return 0
if k == 0 or n == k:
return 1
return doot(n - 1, k - 1) + doot(n - 1, k)
This works fine for small values of n and k. A faster version that doesn't rely on recursion uses factorials, as shown in this answer.
import math
def nCr(n, r):
f = math.factorial
return f(n) / f(r) / f(n - r)
However, calculating the factorial of 13379446 still takes a long time and may not be accurate because the result is so huge. My system hangs when I try it. The other answer to the same question appears to work better.

Categories

Resources