Assignment to variable in indent - python

Very simple problem (At least I think so): So I have a global variable, in this case test, and I want to assign an other value to it in a function. This is my code:
test = "Hello"
def launch():
test = "F"
launch()
print(test)
I wanted test to equal "F", but it doesn't. Instead, the output of this is that test still equals "Hello". I assume it creates a variable inside the function and then assigns a value to it.
But why does this work then:
test = {
"?": "Hallo"
}
def launch():
test["?"] = "F"
launch()
print(test)
The output of this is how I wanted it to be: test["?"] is equal to "F".
How is this working and why isn't the first code example?
Is there any way to use the first code how I wanted it to work?

You need to add a global test line to the top of the function to tell it to reference an existing variable instead of creating a new one. It's ambiguous otherwise.
Why does the second one work though? Because it isn't ambiguous. From context, it knows that you must be referencing an existing variable instead of attempting to create a new one.
test["?"] = "F"
is essentially
test.__setItem__("?", "F")
and that only makes sense if test already exists. It knows that you aren't trying to create a new local variable.
The language only requires a global test statement in cases where it can't tell whether or not you want to use an existing variable or create a new one.

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, however, it’s assumed to be a local unless explicitly declared as global.
So, change it as follows:
test = "Hello"
def launch():
global test
test = "F"

In your second example you don't declare the dictionary (like test = {}). Hence, python looks for a global variable test. You can access global variables by:
def launch():
global test
test = "F"

In the first case, you're trying to access a global variable but you have also redeclared it as a local variable.
test = "Hello" #global variable by default.
def launch():
test = "F" # local variable by default. It's restricted to the function launch().
launch()
print(test) # Accessing global variable because print can't see test of launch.
test = "Hello" #global variable by default.
def launch():
global test
test = "F" # Accessing global value of test.
launch()
print(test) # Accessing global variable test.
In second case,you're assigning to a global variable, not redeclaring it as a local variable. There in lies the difference.
test = {'?':"Hello"} #global variable by default.
def launch():
test['?'] = "F" # global variable access by default. It's NOT restricted to the function launch().
launch()
print(test) # Accessing global variable.

In your first test, in your launch function, the test variable is recreated, with no link to the global function. It's a particularity of python.
If you wanted to change the global function you should have done like this :
def launch():
global test
test = 'F'
With your second test, test is a dictionary.
And yes, when you change a dictionary in a function without declaring it as a global variable, it changes the dictionary outside of the function as well.
If you want to know the real reason, it's way more complicated if you don't know C language... Python was programmed in C (for CPython at least), and in C, managing variables is very different than in Python.
In C, when you want to modify a variable in a function, you have to give to the function the memory address of the variable as a parameter (that variable is called a Pointer).
If you don't give the pointer of your global variable to the function where you want to modify it, it won't work.
When your launch function was called in your first example, test wasn't modified, because as you haven't declared test as global, python hasn't given the pointer of test to the launch function (only its value).
When you use dictionaries, Python will also give the value of the dictionary, but it will be a pointer to its elements. So you will be able to modify them.
That's why you can modify the elements in the dictionary, but you can't modify the dictionary. Example :
a = {"a":123, 'b':456}
def modif():
a = {"aa":123}
modif()
print(a)
results in:
{'a': 123, 'b': 456}
So the dictionary hasn't been modified. It works the same way with the lists (and I suppose with every iterable that support item assignment)
I tried to simplify, it's more complicated in reality, but it may teach you some thing I hope.

Related

assignment of variable after function definition in Python

I am new to Python so I am unsure about the difference in variable assignment before or after the function definition.
Specifically, the first example was adopted from Lutz's book.
def tester(start):
print("inside tester")
def nested(label):
print("inside nested")
print(label,nested.state)
nested.state += 1
print("done with nested")
nested.state = start
print("done with tester")
return nested
F = tester(0)
F('spam')
F.state
F.state
The objective of the code is to store the state information without using nonlocal.
I am unsure what nested.state means here. I am unsure because nested.state is used inside nested() function (i.e. nested.state +=1) and outside nested() function (i.e. nested.state = start).
I modified the code above to see whether Python accepts assigning variable after function declaration for nested() and to see whether there is any concept I am missing relating to function.variable call (i.e. nested.state call).
def tester(start):
def nested(label):
print(label, state)
state += 1 #replaced 'nested.state' with 'state' here
state = start #replaced 'nested.state' with 'state' here
return nested
F=tester(0)
F('spam')
F('ham')
Unfortunately, above code generates error local variable 'state' referenced before assignment. This tells me that I am missing some concept about function.variable (i.e. nested.state).
Can someone please help me understand three things:
I. why it is that the code with nested.state doesn't generate any error but state does?
II. what does nested.state mean? If nested.state is a mechanism to access function's variables, why is it that the call inside nested() function also uses nested.state and not state?
III. If nested.state is a mechanism to access variable inside function, then why is it that PyCharm fails to show state under dropdown when I type nested.?
I'd appreciate any help. I research SO, and couldn't find any explanation on such problems.
The reason the first code example worked is because it was assigning and referencing an attribute of the nested function object. The key concept to understand here, is that Python allows you to assign new, arbitrary attributes to objects - including functions:
>>> def func(a, b):
return a + b
>>> func(1, 2)
3
>>> func.attr = 5
>>> func.attr
5
The first code example takes advantage of this fact by using the nested function object to store the necessary state. This is the same concept as using any other object to store the state. It's particularly convenient, however, to use a function since it's readily available.
In the second example, a normal variable is used. Because of this, normal scoping rules apply which means simply that the state variable defined in tester is not the state variable being referenced in nested. Thus, an error is raised.
Actually, I think you're asking a question about scope in Python, ignoring your code, check this:
def scope_level_1():
variable = 'Scope_level_1'
def scope_level_2():
variable = 'Scope_level_2'
def core():
nonlocal variable
variable += '_TOUCHED_AND_MODIFIED_BY_CORE'
print(variable)
return core()
return scope_level_2()
scope_level_1()
# 'Scope_level_2_TOUCHED_AND_MODIFIED_BY_CORE'
Don't worry about the keyword nonlocal, treat it just as a declaring to make code more readable.
First, remember a += b is the same as a = a + b. So a must exist before getting to the +=.
Simply put, in the first example the function nested has an attribute called state (accessed by nested.state). It is an attribute, which means that once you tell nested that it has an attribute called state (you are doing this in line 9 when nested.state = start) it keep that attribute. So, in the first example nested.state exists when you get to the +=.
In the second example, you are declaring a variable called state in tester, and another variable called state in nested. The one in nested could be called potato for all that matters, because it is not the same variable. Therefore when you arrive to the +=, the variable state does not exist!

Dictionary not assigning value to variable

I am trying to assign a value from a dictionary to a variable but the variable remains unchanged. The value is another dictionary.
The code I used to generate the dictionaries can be found here: http://pastebin.com/Q2Hc8Ktp
I wrote it myself and tested it without this problem.
Here is the code snipit of me trying to copy the dictionary from the dictionary.
_classes = {}
def populateClasses():
print "Classes Exist"
cp = Preferences(''.join([resource_path,"resources.ini"]))
print cp
_classes = cp.getPreferences()['Classes']
populateClasses()
print _classes
When I print out cp it shows the correct data but when I try to print _classes it only shows {}
Note: printing _classes from within the function works as expected but not from outside the function. _classes is defined in the global scope
-Edit-
Here is also some sample data:
[Classes]
Wizard = Arcana, Bluff
Warrior = Endurance, Intimidate
Ranger = Nature, Perception
Bard = Heal, History
If you want to change the value of the global variable _classes, you need to use global:
def populateClasses():
global _classes # <<<<<< THIS
print "Classes Exist"
cp = Preferences(''.join([resource_path,"resources.ini"]))
print cp
_classes = cp.getPreferences()['Classes']
Without this, your method creates a separate local variable also called _classes. This variable goes out of scope as soon as your method returns.
Rather than global, you might use _classes.update(cp.getPreferences()['Classes']).
The rule for globals is that you need the global keyword if you write to it, but don't need it if you're just accessing the variable (even if that access is mutating the state). That's why my suggestion above doesn't need a global statement, and the original code does. Possibly that's also what happened when you "assigned lists to two variables" and didn't need global.

Globals as function input instead arguments

I'm just learning about how Python works and after reading a while I'm still confused about globals and proper function arguments. Consider the case globals are not modified inside functions, only referenced.
Can globals be used instead function arguments?
I've heard about using globals is considered a bad practice. Would it be so in this case?
Calling function without arguments:
def myfunc() :
print myvalue
myvalue = 1
myfunc()
Calling function with arguments
def myfunc(arg) :
print arg
myvalue = 1
myfunc(myvalue)
I've heard about using globals is considered a bad practice. Would it be so in this case?
It depends on what you're trying to achieve. If myfunc() is supposed to print any value, then...
def myfunc(arg):
print arg
myfunc(1)
...is better, but if myfunc() should always print the same value, then...
myvalue = 1
def myfunc():
print myvalue
myfunc()
...is better, although with an example so simple, you may as well factor out the global, and just use...
def myfunc():
print 1
myfunc()
Yes. Making a variable global works in these cases instead of passing them in as a function argument. But, the problem is that as soon as you start writing bigger functions, you quickly run out of names and also it is hard to maintain the variables which are defined globally. If you don't need to edit your variable and only want to read it, there is no need to define it as global in the function.
Read about the cons of the global variables here - Are global variables bad?
There are several reasons why using function arguments is better than using globals:
It eliminates possible confusion: once your program gets large, it will become really hard to keep track of which global is used where. Passing function arguments lets you be much more clear about which values the function uses.
There's a particular mistake you WILL make eventually if you use globals, which will look very strange until you understand what's going on. It has to do with both modifying and reading a global variable in the same function. More on this later.
Global variables all live in the same namespace, so you will quickly run into the problem of overlapping names. What if you want two different variables named "index"? Calling them index1 and index2 is going to get real confusing, real fast. Using local variables, or function parameters, means that they all live in different namespaces, and the potential for confusion is greatly reduced.
Now, I mentioned modifying and reading a global variable in the same function, and a confusing error that can result. Here's what it looks like:
record_count = 0 # Global variable
def func():
print "Record count:", record_count
# Do something, maybe read a record from a database
record_count = record_count + 1 # Would normally use += 1 here, but it's easier to see what's happening with the "n = n + 1" syntax
This will FAIL: UnboundLocalError: local variable 'record_count' referenced before assignment
Wait, what? Why is record_count being treated as a local variable, when it's clearly global? Well, if you never assigned to record_count in your function, then Python would use the global variable. But when you assign a value to record_count, Python has to guess what you mean: whether you want to modify the global variable, or whether you want to create a new local variable that shadows (hides) the global variable, and deal only with the local variable. And Python will default to assume that you're being smart with globals (i.e., not modifying them without knowing exactly what you're doing and why), and assume that you meant to create a new local variable named record_count.
But if you're accessing a local variable named record_count inside your function, Python won't let you access the global variable with the same name inside the function. This is to spare you some really nasty, hard-to-track-down bugs. Which means that if this function has a local variable named record_count -- and it does, because of the assignment statement -- then all access to record_count is considered to be accessing the local variable. Including the access in the print statement, before the local variable's value is defined. Thus, the UnboundLocalError exception.
Now, an exercise for the reader. Remove the print statement and notice that the UnboundLocalError exception is still thrown. Can you figure out why? (Hint: before assigning to a variable, the value on the right-hand side of the assignment has to be calculated.)
Now: if you really want to use the global record_count variable in your function, the way to do it is with Python's global statement, which says "Hey, this variable name I'm about to specify? Don't ever make it a local variable, even if I assign to it. Assign to the global variable instead." The way it works is just global record_count (or any other variable name), at the start of your function. Thus:
record_count = 0 # Global variable
def func():
global record_count
print "Record count:", record_count
# Do something, maybe read a record from a database
record_count = record_count + 1 # Again, you would normally use += 1 here
This will do what you expected in the first place. But hopefully now you understand why it will work, and the other version won't.
It depends on what you want to do.
If you need to change the value of a variable that is declared outside of the function then you can't pass it as an argument since that would create a "copy" of that variable inside the functions scope.
However if you only want to work with the value of a variable you should pass it as an argument. The advantage of this is that you can't mess up the global variable by accident.
Also you should declare global variable before they are used.

Python: local variable 'string' referenced before assignment

I was wondering why i was getting this error for adding a letter to this string from a function.
local variable 'string' referenced before assignment
CODE
def update_string():
string+='d'
string='s'
update_string()
You are accessing global variable, need to declare it:
def update_string():
global string # <<< declare `string` as global variable.
string+='d'
string='s'
update_varibles()
There is nowhere for the old 'string' to come from in the local scope of your function, so python assumes you're talking about the one from the outer scope.
Moreover, since strings are immutable the usual pattern is to create a new one and return it, so you might prefer to update your function interface to something more like:
def update_string(str_in):
return str_in + 'd'
And then you would use it instead like:
my_string = update_string(my_string)
You can access the variable from the outer scope inside the function, but you can't assign to it. So the following is a workaround without using global variables or inputs to the function:
def update_string():
updated=string+"d"
return updated
string="s"
string=update_string()

How to create module-wide variables in Python? [duplicate]

This question already has answers here:
Using global variables in a function
(25 answers)
Closed 3 years ago.
The community reviewed whether to reopen this question 4 months ago and left it closed:
Original close reason(s) were not resolved
Is there a way to set up a global variable inside of a module? When I tried to do it the most obvious way as appears below, the Python interpreter said the variable __DBNAME__ did not exist.
...
__DBNAME__ = None
def initDB(name):
if not __DBNAME__:
__DBNAME__ = name
else:
raise RuntimeError("Database name has already been set.")
...
And after importing the module in a different file
...
import mymodule
mymodule.initDB('mydb.sqlite')
...
And the traceback was:
...
UnboundLocalError: local variable 'DBNAME' referenced before assignment
...
Any ideas? I'm trying to set up a singleton by using a module, as per this fellow's recommendation.
Here is what is going on.
First, the only global variables Python really has are module-scoped variables. You cannot make a variable that is truly global; all you can do is make a variable in a particular scope. (If you make a variable inside the Python interpreter, and then import other modules, your variable is in the outermost scope and thus global within your Python session.)
All you have to do to make a module-global variable is just assign to a name.
Imagine a file called foo.py, containing this single line:
X = 1
Now imagine you import it.
import foo
print(foo.X) # prints 1
However, let's suppose you want to use one of your module-scope variables as a global inside a function, as in your example. Python's default is to assume that function variables are local. You simply add a global declaration in your function, before you try to use the global.
def initDB(name):
global __DBNAME__ # add this line!
if __DBNAME__ is None: # see notes below; explicit test for None
__DBNAME__ = name
else:
raise RuntimeError("Database name has already been set.")
By the way, for this example, the simple if not __DBNAME__ test is adequate, because any string value other than an empty string will evaluate true, so any actual database name will evaluate true. But for variables that might contain a number value that might be 0, you can't just say if not variablename; in that case, you should explicitly test for None using the is operator. I modified the example to add an explicit None test. The explicit test for None is never wrong, so I default to using it.
Finally, as others have noted on this page, two leading underscores signals to Python that you want the variable to be "private" to the module. If you ever do an import * from mymodule, Python will not import names with two leading underscores into your name space. But if you just do a simple import mymodule and then say dir(mymodule) you will see the "private" variables in the list, and if you explicitly refer to mymodule.__DBNAME__ Python won't care, it will just let you refer to it. The double leading underscores are a major clue to users of your module that you don't want them rebinding that name to some value of their own.
It is considered best practice in Python not to do import *, but to minimize the coupling and maximize explicitness by either using mymodule.something or by explicitly doing an import like from mymodule import something.
EDIT: If, for some reason, you need to do something like this in a very old version of Python that doesn't have the global keyword, there is an easy workaround. Instead of setting a module global variable directly, use a mutable type at the module global level, and store your values inside it.
In your functions, the global variable name will be read-only; you won't be able to rebind the actual global variable name. (If you assign to that variable name inside your function it will only affect the local variable name inside the function.) But you can use that local variable name to access the actual global object, and store data inside it.
You can use a list but your code will be ugly:
__DBNAME__ = [None] # use length-1 list as a mutable
# later, in code:
if __DBNAME__[0] is None:
__DBNAME__[0] = name
A dict is better. But the most convenient is a class instance, and you can just use a trivial class:
class Box:
pass
__m = Box() # m will contain all module-level values
__m.dbname = None # database name global in module
# later, in code:
if __m.dbname is None:
__m.dbname = name
(You don't really need to capitalize the database name variable.)
I like the syntactic sugar of just using __m.dbname rather than __m["DBNAME"]; it seems the most convenient solution in my opinion. But the dict solution works fine also.
With a dict you can use any hashable value as a key, but when you are happy with names that are valid identifiers, you can use a trivial class like Box in the above.
Explicit access to module level variables by accessing them explicity on the module
In short: The technique described here is the same as in steveha's answer, except, that no artificial helper object is created to explicitly scope variables. Instead the module object itself is given a variable pointer, and therefore provides explicit scoping upon access from everywhere. (like assignments in local function scope).
Think of it like self for the current module instead of the current instance !
# db.py
import sys
# this is a pointer to the module object instance itself.
this = sys.modules[__name__]
# we can explicitly make assignments on it
this.db_name = None
def initialize_db(name):
if (this.db_name is None):
# also in local function scope. no scope specifier like global is needed
this.db_name = name
# also the name remains free for local use
db_name = "Locally scoped db_name variable. Doesn't do anything here."
else:
msg = "Database is already initialized to {0}."
raise RuntimeError(msg.format(this.db_name))
As modules are cached and therefore import only once, you can import db.py as often on as many clients as you want, manipulating the same, universal state:
# client_a.py
import db
db.initialize_db('mongo')
# client_b.py
import db
if (db.db_name == 'mongo'):
db.db_name = None # this is the preferred way of usage, as it updates the value for all clients, because they access the same reference from the same module object
# client_c.py
from db import db_name
# be careful when importing like this, as a new reference "db_name" will
# be created in the module namespace of client_c, which points to the value
# that "db.db_name" has at import time of "client_c".
if (db_name == 'mongo'): # checking is fine if "db.db_name" doesn't change
db_name = None # be careful, because this only assigns the reference client_c.db_name to a new value, but leaves db.db_name pointing to its current value.
As an additional bonus I find it quite pythonic overall as it nicely fits Pythons policy of Explicit is better than implicit.
Steveha's answer was helpful to me, but omits an important point (one that I think wisty was getting at). The global keyword is not necessary if you only access but do not assign the variable in the function.
If you assign the variable without the global keyword then Python creates a new local var -- the module variable's value will now be hidden inside the function. Use the global keyword to assign the module var inside a function.
Pylint 1.3.1 under Python 2.7 enforces NOT using global if you don't assign the var.
module_var = '/dev/hello'
def readonly_access():
connect(module_var)
def readwrite_access():
global module_var
module_var = '/dev/hello2'
connect(module_var)
For this, you need to declare the variable as global. However, a global variable is also accessible from outside the module by using module_name.var_name. Add this as the first line of your module:
global __DBNAME__
You are falling for a subtle quirk. You cannot re-assign module-level variables inside a python function. I think this is there to stop people re-assigning stuff inside a function by accident.
You can access the module namespace, you just shouldn't try to re-assign. If your function assigns something, it automatically becomes a function variable - and python won't look in the module namespace.
You can do:
__DB_NAME__ = None
def func():
if __DB_NAME__:
connect(__DB_NAME__)
else:
connect(Default_value)
but you cannot re-assign __DB_NAME__ inside a function.
One workaround:
__DB_NAME__ = [None]
def func():
if __DB_NAME__[0]:
connect(__DB_NAME__[0])
else:
__DB_NAME__[0] = Default_value
Note, I'm not re-assigning __DB_NAME__, I'm just modifying its contents.

Categories