python local variable- when do I have to assign a value? - python

I'm an amateur programmer and would like to seek advice while learning codes. Here I encounter some issues.
I found that when I remove the comment "#X=3" and make it into a code from the below then the code works. Without X=3, the code results into UnboundLocalError.
Browsed online, it's related to global and local variable but I can't see how it's related. And when does X has to be denoted before the while loop? and why "for y in primes" doesn't need to pre-define "y"?
Main purpose of the code: count the # of prime numbers up to (num)
def count_primes2(num):
primes = [2]
#x = 3
if num < 2:
return 0
while x <= num:
for y in primes: # use the primes list!
if x%y == 0:
x += 2
break
else:
primes.append(x)
x += 2
print(primes)
return len(primes)

As per design pattern variable should be created just before to use. In the code you are using x without creating or initializing default value.
"y" = you are iterating the list (primes). So in each iteration y will be initialized by the current value.So it will not give any error.

To expand, since you are using x in the while loop criteria, yes, it has to be defined before. You don't need to define y before the for loop because the for y in primes line defines y as each item in the list.
A rough translation to plain English:
while x <= num: == As long as this number is less than or equal to this other number, do the following
for y in primes == do the following for each item, named y, in primes
hopefully that wasn't more confusing

You need to create (and assign a value to) a variable before you use it. If you try to use a variable's value before creating the variable, then you get the exception. You do exactly this in the while expression: You ask if its value is below or equal to num, but it does not even exist yet, it has no value, this raises the exception.
Now, why do you get the UnboundLocalError?
The compiler goes through your code before the code gets executed. In this compile step it notices that you somewhere in your function assign a value to X, namely in the line x += 2. (There are even two of them.) This marks the variable for the compiler as a local variable.
So if you try to access the variable before the assignment takes place, the variable doesn't exist yet, but the code already knows that is supposed to be a local variable, hence the UnboundLocalError.
Without any assignment statement to X in the function you would have gotten a NameError because during execution of the while statement the interpreter then searches for a global variable of this name.

Related

Mandatory While Loop Gives Local Variable Might Be Referenced Before Assignment

The issue I am having is that it should be impossible not break out of the while loop, AKA "matched_index" will always be defined, yet my IDE is throwing an "may be referenced before assignment" error. What is the Pythonic way to code this? The closest I found was this post, but I'm not sure how to code what kindall is suggesting.
Here is my code:
another_list = [0] * len(my_list)
if X in my_list:
i = 0
while i < len(my_list):
if X == my_list[i]:
matched_index = i
break
i += 1
another_list[matched_index] = 0.2
Just initialize matched_index, or move your assignment statement into the loop (so that you're not using another temporary variable to hold it).
While it might be true that, from a logical perspective, the code won't run into a scenario where len(my_list) == 0, the lexer can't guarantee it.

Difficulty with Fizz Buzz

I'm a high highschool student taking an online course for python, and one of the assignments is to create a function similar to fizz buzz, except instead of "fizz" and "buzz", they simply use "three" and "five". I wrote a function but unfortunately it completely failed, and doesn't do its job at all. I've been having a hard time to figure out what to input instead of "for x in range(101):", I think that's where my problem is. Any pointers/guidance would be very helpful, I'm completely lost.
The instructions are here and here is my code:
def student_func(x):
for x in range(101):
if x % 3 == 0 and x % 5 == 0:
return('threefive')
elif x % 3 == 0:
return('three')
elif x % 5 == 0:
return('five')
else:
return x
Edit:A couple of people were recommending me to delete the loop, since it was starting off the function with 0(I should've realized that). However when I took it out the output became None. Am I doing something else wrong, or am I misunderstanding? Thanks for the speedy answers however, I'm still very inexperienced
Code with new error:
def student_func(x):
if x % 3 == 0 and x % 5 == 0:
return('threefive')
elif x % 3 == 0:
return('three')
elif x % 5 == 0:
return('five')
else:
print(x)
So, a few things to note. First, the range(n) function returns an iterator that will go through 0, 1, 2, ..., n-1. Thus, the first value you will iterate over in your code will be 0. Think about how your for loop will handle that.
Second, because you're assigning x to be the values iterated over from the range function, you're never actually testing the x being passed into the function.
Third, note that every branch of the if-elif-else tree inside the loop has a return statement, thus the loop is only going to go around once before returning a value. That sort of defeats the purpose of using a loop.
Lastly, make sure you are aware of how 0 % n evaluates. Think about things and see if you can reach a conclusion about why your program is returning student_func(1) = 'threefive'.
The issue is that the "tester" which tests your function gives the function the number/argument - x. However, what you are doing is testing the function from 0 - 100. The question does not ask you to test your function with numbers from 0 - 100. Therefore, you should remove the for loop.
Why is it giving the output "threefive"? Well, the function first tests in the loop the number 0. It satisfies the first if statement, and returns (so it gets out of the function).
Right now your code is looping through every value from 0 to 100 and returning if the first value (0) is divisible by both three and five, which of course it is. The function is only asking you to check if a single value is divisible by three, or five, or both. Why would you need a loop?
Your function isn't supposed to loop; it's supposed to examine only the single value passed in as the argument. The teacher's function will call your function with a variety of inputs.
Take out the for loop statement and it should work.

Project Euler #4 with python. What;s wrong with my code?

I am trying to do project euler problem 4 using python. The problem statement goes like this:
A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 × 99.
Find the largest palindrome made from the product of two 3-digit numbers.
I wrote down a solution for it:
s=0
x=100
y=100
list=[]
z=x*y
def palindrome():
while z>=1:
s=s*10+z%10
z=z/10
if z==s:
list.append(z)
while x<=999:
while y<=999:
palindrome()
y=y+1
x=x+1
y=100
print list
It ended up giving an error along the lines of 'z referenced beyond assignment'.
I searched for a solution to this error before finally deciding to use the syntax 'global' to bypass this error.
s=0
x=100
y=100
list=[]
z=x*y
def palindrome():
global z
global s
global x
global y
global list
while z>=1:
s=s*10+z%10
z=z/10
if z==s:
list.append(z)
while x<=999:
while y<=999:
palindrome()
y=y+1
x=x+1
y=100
print list
Now it doesn't give an error, but it gives an empty list as output. I tried to debug the code by inserting print statements in between. The loops appear to work fine, as 'x' and 'y' print all the values they are supposed to. However, I get an empty list as an output to the print list command and 'z' does not apparently change values and is stuck at 100000 despite me using while loops to change the values of x and y.
I am at a loss on how to proceed from here.
The error you got was probably:
UnboundLocalError: local variable 'z' referenced before assignment
This means that z was not defined, at least not within the palindrome() function. Your solution of adding the global keyword is technically correct. However, as others have pointed out already, use of globals makes the code hard to follow.
It's not clear to me what palindrome() is supposed to do. Is it supposed to check if a number is a palindrome? Generate palindrome numbers? To fix this problem, you should think about structuring your code. There are many ways to do this, of course, and with time you will find your own style.
My advice, then, is to think about how you would solve this in general. If you don't know the solution, coding won't help you. Sometimes, when solving problems like this one, I write functions without declaring their bodies. You can do this top-down or bottom-up, both work. For example:
def is_palindrome(n):
""" Check if n is a palindrome number. """
pass
def multiples_of_3_digits():
""" Return all numbers that are the product of two 3-digit numbers ."""
pass
def main():
print max(n for n in multiples_of_3_digits() if is_palindrome(n))
This way you can focus on solving the problem, then on the actual coding. Maybe you will add helper functions or realize you can solve the problem in a more efficient way, but it's a start. Good luck!
min=100
max=999
max_palindrome = 0
for a in range(min,max + 1):
for b in range(a + 1, max + 1):
prod = a*b
if prod > max_palindrome and str(prod)==(str(prod)[::-1]):
max_palindrome = prod
print max_palindrome
Here we are only concerned with the maximum palindrome, and so we don’t spend any time storing other palindromes once they are known to be non-maximum. Also, the if statement first checks if the given product is larger than the maximum known palindrome before using the string cast and list slice to check whether or not the number is even a palindrome. This should speed up our code a bit since the greater than comparison will often fail, regardless of whether the product in question is a palindrome. When we run this, we get the following.
906609
Alternate Way:
I would discourage you to use the global variables because of the reasons pointed out by others. I would also like you to refer to Andre's approach as it will teach you to organize yourself. In this approach too I will be using 2 functions is_palindrome(num) [to check if the number is palindrome or not] and find_max_palindrome [to find the largest palindrome]
def is_palindrome(num):
reversed = 0
original = num
if num < 10:
return True
if num % 10 == 0:
return False
while num >= 1:
reversed = (reversed * 10) + (num % 10)
num = num/10
if original == reversed:
return True
else:
return False
def find_max_palindrome():
max_palindrome = 0
a = 999
b = 999
prod = 0
while a > 99:
b = 999
while b >= a:
prod = a*b
if prod > max_palindrome and is_palindrome(prod):
max_palindrome = prod
b = b -1
a = a - 1
return max_palindrome
print find_max_palindrome()

How do I double my step variable for each for-loop iteration in Python 3?

I'm new to Python, and I'm playing around with recursive functions just for practice.
I made the following algorithm which takes a number as x and halves it until it is equal to 1. n is the number of times x has been halved.
def binary(x, n = 0):
print(n,":",x)
x = x // 2
n += 1
if x > 0:
binary(x, n)
return x
return x
I'm trying to make a loop that will call binary() with multiple values for x. I want my step to be doubled each iteration. I have it working with a while loop like the one below.
i = 1
while i < 1000000000:
print("when x is", i, ":")
binary(i)
i += i
For some reason though, I can't seem to achieve the same thing with a For loop. Here's what I have now.
for i in range(1,1000):
print("when x is", i, ":")
binary(i)
i += i
In the code above, i += i does not seem to effect the i in my header. I know that range() takes a third parameter called step, but I've tried this:
for i in range(1,1000, i += i):
# statements
This gives me a name error, and says "i is not defined".
Most of my experience with programming is in JavaScript and C#. In both of those languages I wouldn't of had any trouble doing this.
How would I get this to work in a For loop using Python 3?
The third parameter of range is indeed step. But you should use it with a computed value like:
for i in range(1,1000,2):
#statements
The reason why your i += i didn't worked is because, under the hood, the for-loop is executing something similar to i = next(...) at the end of an iteration, overiding your last increment.
Edit
You can achieve the desired effect using a generator, but it kinda kills the "trying to avoid while-loops" thing:
def myrange(start, stop):
i = start
while i < stop:
yield i
i += i
for i in myrange(1, 1000):
print(i)
Anyway, while-loops are perfectly valid constructs and I’d personnally go with one in this case. Do not forget that the for-loop has a rather different semantic in python than in both languages you’re used to. So trying to use a for-loop because you are able to do so with javascript seems like a bad idea if all what you need is a while-loop.
range can step by a fixed amount, but not a variable amount. Use a while-loop to increment i by i:
i += i
You could replace the while-loop with an iterator, such as:
import itertools as IT
for i in (2**i for i in IT.count()):
if i >= 1000000000: break
print("when x is", i, ":")
binary(i)
but I don't think this has any advantage over a simple while-loop.
If all you're doing is doubling i, then why not just raise it to the power?
for p in range(int(1000000000**0.5)):
print(binary(2**p)

Taking the maximum of an arbitrary amount of arguments in Python

I have very little experience in coding and I'm learning Python in a class. I'm learning about conditionals and loops and have been asked to create a function that'll take an arbitrary amount of arguments and give me the maximum. Obviously I'm not allowed to use the built-in max function.
So far, I have:
def max(x):
current_max = x[1]
for i in x[i]:
if x[i] > current_max:
current_max = x[i]
When I run the code, it gives me no errors, but when I try to run max() it'll only accept one argument. The only idea that came to mind was adding in:
x = input('Enter numbers to compare:')
When I ran this, I got:
UnboundLocalError: local variable 'i' referenced before assignment
And I'm unsure of what I can do at this point as I'm unsure of whether or not I'm not defining the argument correctly or there's just an error in the code defining max(x) that didn't show up in the first time for some reason.
Two things:
1) In Python, you need to add a single * before the argument to indicate that the argument is actually a list of arguments. Without that *, you're function is only going to expect a single argument. EDIT: Ah I just saw you are actually passing a list object into your function, in which case the * isn't needed. If you wanted to support a call such as max(1,3,7,-4) then you would indeed want a *.
2) When iterating through a list, or any other "iterable", you can use for item in list_of_items: to iterate over and examine each item in the list. This is used all the time in Python and is the preferred way (ie. the Pythonic way) to iterate over the list of args in a case such as this.
Here's an example max function that ties it all together (and supports a call such as max(1,5,7,3):
def max(*x):
current_max = x[0] # Note we're assuming at least one argument was passed in. What if nothing was passed in?
for i in x:
if i > current_max:
current_max = i
return current_max
And here's an example max function that supports a call such as max([1,5,7,3]):
def max(x):
current_max = x[0] # Note we're assuming at least one argument was passed in. What if nothing was passed in?
for i in x:
if i > current_max:
current_max = i
return current_max
FYI My examples are not the best solutions to this problem, so I suggest you expand on them for your final solution.
x[i] is executed before the loop is for processed by the compiler so trying to loop through x[i] without i being declared beforehand will result in an error.
Given x where x is the list x = [ 1,2,3,4 ] the following should work
def max(x):
current_max = x[0]
for i in x:
if i > current_max:
current_max = i
return current_max
x = [1,2,3,4]
print max(x)
Your algorithm will work but there are two small problems. First, there is a problem with iterating over an array. Normally you don't need indexes at all, just do:
some_list = [1, 2, 3]
for item in some_list:
print item
# Will print 1, then 2 and finally 3
Also you need to return current_max when you are done with your loop.
Reusing names of builtin functions is not a great idea, so I'd call it my_max or something like that. If you also want to check your arguments and add a docstring, you'll end up with this:
def my_max(x):
""" Return the biggest item in an iterable. """
if len(x) == 0:
raise ValueError("my_max() arg is an empty sequence")
current_max = x[0]
for i in x:
if i > current_max:
current_max = i
return current_max
If you prefer a functional coding style, you can use reduce to reduce your list to the biggest element. Simply define the lambda that returns the biggest element:
def my_max(xs):
return reduce(lambda x, y: x if x > y else y, xs)
If you wonder how Python's built-in max is defined, it's actually written in C like many of Python's built-in functions. You can check it out on GitHub if you're curious. Internally it works just as min, since comparisons are done using something called PyObject_RichCompareBool that can compare two objects in different ways (less, less or equal, equal, not equal, ...)

Categories

Resources