Defining variable scope in function - python

I'm attempting to loop through a function and use a variable defined in the main scope of the program, but for some reason, it's not passed into my function. This is the first time I've used functions and variable scopes in Python, I read through the Python documentation as well as some various posts on here, but couldn't seem to figure out what I did wrong.
The function will be recursive therefore I'm unable to define them in the head of the function else it will just redefine each time. I tried doing what was done in this post in my file, but it doesn't seem to work.
I have both the main + function in one file and defined the variables I wish to use as global inside the function I want to use them in.
lv1Count = 12
lv2Count = 14
lv3Count = 18
lv4Count = 4
AL = []
def opt(target):
global lv4Count
global lv3Count
global lv2Count
global lv1Count
global AL
goal = target
if (goal <= 0 & lv4Count < 0):
pass
if (goal <= 1 & lv1Count < 0):
pass
if (goal == 2 & lv2Count < 0):
pass
if (goal == 3 & lv3Count < 0):
pass
if (goal == 4 & lv4Count < 0):
pass
opt(4)
I replaced all of the if statements with pass to avoid excessive code, but essentially whenever returning something from these statements, the comparison using the counter doesn't work as it's not successfully reading the value of this variable and the functionality doesn't occur.

Your function is working correctly: this is indeed how you use global variables, even though it is usually a bad idea. (In recursion, it is most common to pass the necessary values as arguments to the function.) If you include more details about what kind of recursion you want to do, I can help with that.
In [1]: v = 1
In [2]: def test():
...: global v
...: return v
...:
In [3]: test()
Out[3]: 1
The problem is with your if statement: you are using bitwise & instead of the normal logical operator and. Since & is evaluated first in the order of operations, you are getting problems. Consider:
In [1]: bool(1 == 1 & 2 == 2)
Out[1]: False
Why? Because this is evaluated as:
In [1]: bool(1 == (1 & 2) == 2)
Out[1]: False
Which is the same as:
In [1]: bool(1 == 0 == 2)
Out[1]: False

Related

Python while loop UnboundLocalError

I need a function with a while loop, that as long as the condition is False calls the function again.
This code will obviously result in a endless loop since "i" is always re declared as "0":
def fun():
i = 0
# does something else
while i < 5:
i += 1
fun()
print('done')
fun()
So I see no other way than to go about it like this:
i = 0
def fun():
# does something else
while i < 5:
i += 1
fun()
print('done')
fun()
But with this code It says local variable 'i' referenced before assignment.
I don't get it what am I doing wrong here?
I know I could use an if statement here, but that would be much more complicated and can get in my way in the project, although its not impossible.
Basically, the value i has not been declared in the function, so the function doesn't know what i is. There are a couple of ways to fix this.
One way is to put the while loop OUTSIDE of the function. This is how it would look like:
i = 0
def fun():
#Do Something...
print('done')
while i < 5:
i += 1
fun()
Another way would be to add the parameter i into the function. You do this by adding i into the brackets. This is how it would look like:
i = 0
def fun(i): #I Added An i Here So That The Function Knows We Need A Value Of i
#Do Something...
while i < 5:
i += 1
fun(i) #I Use The Same Value Of i Set At The Beginning
print('done')
fun(i) #I Call The Function Again
You could also set i as a global variable, meaning it can be used in the function too. To do that, just add global and the variable. This is how I did it:
i = 0
def fun():
global i
#Do Something...
while i < 5:
i += 1
fun()
print('done')
fun()
If neither of these 3 methods work, you could try and maybe use a for loop: something like for i in range(0, 5): , which would work too.
variable i is not in the scope of the function. Hence it is not accessible.
I believe the below code will help you get the desired output.
def fun(count):
# does something else
if(count >= 5):
return
fun(count + 1)
print('done')
fun(0)
Since other posts has offered remedy already, here I just point out the reason more detail:
This is one of the common gotchas to the new learners, the reason is that -
it's because when you make an assignment to a variable in a scope, that variable becomes local to that scope and shadows any similarly named variable in the outer scope.
In Python, variables that are only referenced inside a function are implicitly global. If a variable is assigned a value anywhere within the function’s body, it’s assumed to be a local unless explicitly declared as global.
It can be a surprise to get the UnboundLocalError in previously working code when it is modified by adding an assignment statement somewhere in the body of a function.
hope this is what you are looking for:
def fun(i=0):
# does something else
result = True
while result:
print(i)
i = i + 1
while i >= 5:
result = False
break
print('done')
fun()

Making a variable conditionally global

Suppose that I'm required to run a function with a loop in it until I meet a condition. For the function to work right, I'm only allowed to return one value in the function, but once the condition clears, I want to bring some of the calculations I performed into the global scope. I'm not allowed to use the return command to do this, so I decided to globalize the variables in post. This raises a warning, but seems to work alright. Is this the best way to do things?
Here's an example:
def check_cond(x,cond):
return (x - cond,3)
def loop(x,func):
relevant_value = 0
while x > 0:
local_expensive_calculation = 1 #use your imagination
x = func(x,local_expensive_calculation)[0]
relevant_value += func(x,local_expensive_calculation)[1]
if x == 0:
global local_expensive_calculation
return relevant_value
x = 4
loop(x,check_cond)
#then do stuff with local_expensive_calculation, which is now global
This may be slightly abusing the system but you can set your variable as an attribute of the function, and then access it later through that namespace:
def check_cond(x,cond):
return (x - cond,3)
def loop(x,func):
relevant_value = 0
while x > 0:
local_expensive_calculation = 1 #use your imagination
x = func(x,local_expensive_calculation)[0]
relevant_value += func(x,local_expensive_calculation)[1]
if x == 0:
loop.local_expensive_calculation = local_expensive_calculation
return relevant_value
x = 4
loop(x,check_cond)
print loop.local_expensive_calculation
If you absolutely insist on having it as a global, one way you can do that is by changing the line:
global local_expensive_calculation
to:
globals()['local_expensive_calculation'] = local_expensive_calculation
It makes your SyntaxWarning disappear.
I don't know much about the context and it appears you want a Python 2.7 answer (Python 3.?) has nonlocal.
Bear in mind that the while statement can also have a else clause
You can write:
while x>0:
# do calculation and stuff
else:
# make calculation result global
PS: There's a typo in your assignment to x: (calcuation --> calculation)

Alternate to global variable in Python

I am implementing a recursive function in which I need to remember a global value. I will decrement this value in every recursive call and want it to reflect in other recursive calls also.
Here's a way I've done it.
First way:
global a
a = 3
def foo():
global a
if a == 1:
print 1
return None
print a
a -= 1 # This new 'a' should be available in the next call to foo()
foo()
The output:
3
2
1
But I want to use another way because my professor says global variables are dangerous and one should avoid using them.
Also I am not simply passing the variable 'a' as argument because 'a' in my actual code is just to keep track of some numbers, that is to track the numbering of nodes I am visiting first to last. So, I don't want to make my program complex by introducing 'a' as argument in every call.
Please suggest me whatever is the best programming practice to solve the above problem.
Don't use a global; just make a a parameter to the function:
def foo(a):
print a
if a == 1:
return None
foo(a-1)
foo(3)
Try this :
Use a parameter instead of a global variable.
Example code
a = 3
def foo(param):
if param == 1:
print 1
return None
print param
foo(param - 1)
foo(a)

How to change the look of Python operators?

In order to make Python look more familiar, I've tried to assign an operator symbol to a variable's name,just for educational purposes: import operator equals = operator.eq
This seems to work fine for equals(a,b) but not for a equals b
Is there a way to express that a equals b instead of a == b
No, Python (and most mainstream languages) does not allow this kind of customization. In Python the restriction is quite intentional — an expression such as a equals b would look ungrammatical to any reader familiar with Python.
Not necessarily, but another SO answer shows how you can use this simple trick to "create" new operators. However, they only work if you surround the operator by | | or by << >>:
equals = Infix(lambda x, y: x == y):
print 2 |equals| 2 # True
The best you can do is
def equals(a,b):
return a == b
equals(1,5)
>> False
or
class my:
value = 0
def equals(self, b):
return self.value == b
a = my()
a.equals(3)
>>False
But you should use the built-in operator for readability. This way, a reader can distinguish, at once, an operator, a function, a symbol (a variable), a member function, etc...

Behaviour of increment and decrement operators in Python

How do I use pre-increment/decrement operators (++, --), just like in C++?
Why does ++count run, but not change the value of the variable?
++ is not an operator. It is two + operators. The + operator is the identity operator, which does nothing. (Clarification: the + and - unary operators only work on numbers, but I presume that you wouldn't expect a hypothetical ++ operator to work on strings.)
++count
Parses as
+(+count)
Which translates to
count
You have to use the slightly longer += operator to do what you want to do:
count += 1
I suspect the ++ and -- operators were left out for consistency and simplicity. I don't know the exact argument Guido van Rossum gave for the decision, but I can imagine a few arguments:
Simpler parsing. Technically, parsing ++count is ambiguous, as it could be +, +, count (two unary + operators) just as easily as it could be ++, count (one unary ++ operator). It's not a significant syntactic ambiguity, but it does exist.
Simpler language. ++ is nothing more than a synonym for += 1. It was a shorthand invented because C compilers were stupid and didn't know how to optimize a += 1 into the inc instruction most computers have. In this day of optimizing compilers and bytecode interpreted languages, adding operators to a language to allow programmers to optimize their code is usually frowned upon, especially in a language like Python that is designed to be consistent and readable.
Confusing side-effects. One common newbie error in languages with ++ operators is mixing up the differences (both in precedence and in return value) between the pre- and post-increment/decrement operators, and Python likes to eliminate language "gotcha"-s. The precedence issues of pre-/post-increment in C are pretty hairy, and incredibly easy to mess up.
Python does not have pre and post increment operators.
In Python, integers are immutable. That is you can't change them. This is because the integer objects can be used under several names. Try this:
>>> b = 5
>>> a = 5
>>> id(a)
162334512
>>> id(b)
162334512
>>> a is b
True
a and b above are actually the same object. If you incremented a, you would also increment b. That's not what you want. So you have to reassign. Like this:
b = b + 1
Many C programmers who used python wanted an increment operator, but that operator would look like it incremented the object, while it actually reassigns it. Therefore the -= and += operators where added, to be shorter than the b = b + 1, while being clearer and more flexible than b++, so most people will increment with:
b += 1
Which will reassign b to b+1. That is not an increment operator, because it does not increment b, it reassigns it.
In short: Python behaves differently here, because it is not C, and is not a low level wrapper around machine code, but a high-level dynamic language, where increments don't make sense, and also are not as necessary as in C, where you use them every time you have a loop, for example.
While the others answers are correct in so far as they show what a mere + usually does (namely, leave the number as it is, if it is one), they are incomplete in so far as they don't explain what happens.
To be exact, +x evaluates to x.__pos__() and ++x to x.__pos__().__pos__().
I could imagine a VERY weird class structure (Children, don't do this at home!) like this:
class ValueKeeper(object):
def __init__(self, value): self.value = value
def __str__(self): return str(self.value)
class A(ValueKeeper):
def __pos__(self):
print 'called A.__pos__'
return B(self.value - 3)
class B(ValueKeeper):
def __pos__(self):
print 'called B.__pos__'
return A(self.value + 19)
x = A(430)
print x, type(x)
print +x, type(+x)
print ++x, type(++x)
print +++x, type(+++x)
TL;DR
Python does not have unary increment/decrement operators (--/++). Instead, to increment a value, use
a += 1
More detail and gotchas
But be careful here. If you're coming from C, even this is different in python. Python doesn't have "variables" in the sense that C does, instead python uses names and objects, and in python ints are immutable.
so lets say you do
a = 1
What this means in python is: create an object of type int having value 1 and bind the name a to it. The object is an instance of int having value 1, and the name a refers to it. The name a and the object to which it refers are distinct.
Now lets say you do
a += 1
Since ints are immutable, what happens here is as follows:
look up the object that a refers to (it is an int with id 0x559239eeb380)
look up the value of object 0x559239eeb380 (it is 1)
add 1 to that value (1 + 1 = 2)
create a new int object with value 2 (it has object id 0x559239eeb3a0)
rebind the name a to this new object
Now a refers to object 0x559239eeb3a0 and the original object (0x559239eeb380) is no longer refered to by the name a. If there aren't any other names refering to the original object it will be garbage collected later.
Give it a try yourself:
a = 1
print(hex(id(a)))
a += 1
print(hex(id(a)))
In python 3.8+ you can do :
(a:=a+1) #same as ++a (increment, then return new value)
(a:=a+1)-1 #same as a++ (return the incremented value -1) (useless)
You can do a lot of thinks with this.
>>> a = 0
>>> while (a:=a+1) < 5:
print(a)
1
2
3
4
Or if you want write somthing with more sophisticated syntaxe (the goal is not optimization):
>>> del a
>>> while (a := (a if 'a' in locals() else 0) + 1) < 5:
print(a)
1
2
3
4
It will return 0 even if 'a' doesn't exist without errors, and then will set it to 1
Python does not have these operators, but if you really need them you can write a function having the same functionality.
def PreIncrement(name, local={}):
#Equivalent to ++name
if name in local:
local[name]+=1
return local[name]
globals()[name]+=1
return globals()[name]
def PostIncrement(name, local={}):
#Equivalent to name++
if name in local:
local[name]+=1
return local[name]-1
globals()[name]+=1
return globals()[name]-1
Usage:
x = 1
y = PreIncrement('x') #y and x are both 2
a = 1
b = PostIncrement('a') #b is 1 and a is 2
Inside a function you have to add locals() as a second argument if you want to change local variable, otherwise it will try to change global.
x = 1
def test():
x = 10
y = PreIncrement('x') #y will be 2, local x will be still 10 and global x will be changed to 2
z = PreIncrement('x', locals()) #z will be 11, local x will be 11 and global x will be unaltered
test()
Also with these functions you can do:
x = 1
print(PreIncrement('x')) #print(x+=1) is illegal!
But in my opinion following approach is much clearer:
x = 1
x+=1
print(x)
Decrement operators:
def PreDecrement(name, local={}):
#Equivalent to --name
if name in local:
local[name]-=1
return local[name]
globals()[name]-=1
return globals()[name]
def PostDecrement(name, local={}):
#Equivalent to name--
if name in local:
local[name]-=1
return local[name]+1
globals()[name]-=1
return globals()[name]+1
I used these functions in my module translating javascript to python.
In Python, a distinction between expressions and statements is rigidly
enforced, in contrast to languages such as Common Lisp, Scheme, or
Ruby.
Wikipedia
So by introducing such operators, you would break the expression/statement split.
For the same reason you can't write
if x = 0:
y = 1
as you can in some other languages where such distinction is not preserved.
Yeah, I missed ++ and -- functionality as well. A few million lines of c code engrained that kind of thinking in my old head, and rather than fight it... Here's a class I cobbled up that implements:
pre- and post-increment, pre- and post-decrement, addition,
subtraction, multiplication, division, results assignable
as integer, printable, settable.
Here 'tis:
class counter(object):
def __init__(self,v=0):
self.set(v)
def preinc(self):
self.v += 1
return self.v
def predec(self):
self.v -= 1
return self.v
def postinc(self):
self.v += 1
return self.v - 1
def postdec(self):
self.v -= 1
return self.v + 1
def __add__(self,addend):
return self.v + addend
def __sub__(self,subtrahend):
return self.v - subtrahend
def __mul__(self,multiplier):
return self.v * multiplier
def __div__(self,divisor):
return self.v / divisor
def __getitem__(self):
return self.v
def __str__(self):
return str(self.v)
def set(self,v):
if type(v) != int:
v = 0
self.v = v
You might use it like this:
c = counter() # defaults to zero
for listItem in myList: # imaginary task
doSomething(c.postinc(),listItem) # passes c, but becomes c+1
...already having c, you could do this...
c.set(11)
while c.predec() > 0:
print c
....or just...
d = counter(11)
while d.predec() > 0:
print d
...and for (re-)assignment into integer...
c = counter(100)
d = c + 223 # assignment as integer
c = c + 223 # re-assignment as integer
print type(c),c # <type 'int'> 323
...while this will maintain c as type counter:
c = counter(100)
c.set(c + 223)
print type(c),c # <class '__main__.counter'> 323
EDIT:
And then there's this bit of unexpected (and thoroughly unwanted) behavior,
c = counter(42)
s = '%s: %d' % ('Expecting 42',c) # but getting non-numeric exception
print s
...because inside that tuple, getitem() isn't what used, instead a reference to the object is passed to the formatting function. Sigh. So:
c = counter(42)
s = '%s: %d' % ('Expecting 42',c.v) # and getting 42.
print s
...or, more verbosely, and explicitly what we actually wanted to happen, although counter-indicated in actual form by the verbosity (use c.v instead)...
c = counter(42)
s = '%s: %d' % ('Expecting 42',c.__getitem__()) # and getting 42.
print s
There are no post/pre increment/decrement operators in python like in languages like C.
We can see ++ or -- as multiple signs getting multiplied, like we do in maths (-1) * (-1) = (+1).
E.g.
---count
Parses as
-(-(-count)))
Which translates to
-(+count)
Because, multiplication of - sign with - sign is +
And finally,
-count
A straight forward workaround
c = 0
c = (lambda c_plusplus: plusplus+1)(c)
print(c)
1
No more typing
c = c + 1
Also, you could just write
c++
and finish all your code and then do search/replace for "c++", replace with "c=c+1". Just make sure regular expression search is off.
Extending Henry's answer, I experimentally implemented a syntax sugar library realizing a++: hdytto.
The usage is simple. After installing from PyPI, place sitecustomize.py:
from hdytto import register_hdytto
register_hdytto()
in your project directory. Then, make main.py:
# coding: hdytto
a = 5
print(a++)
print(++a)
b = 10 - --a
print(b--)
and run it by PYTHONPATH=. python main.py. The output will be
5
7
4
hdytto replaces a++ as ((a:=a+1)-1) when decoding the script file, so it works.

Categories

Resources