Incrementing an intstance attribute vs incrementing global attributes (scope) [duplicate] - python

This question already has answers here:
Global dictionaries don't need keyword global to modify them? [duplicate]
(2 answers)
Closed 8 years ago.
class blah(object):
def __init__(self):
self.x=5
blahinstance=blah()
def testclass():
blahinstance.x+=1
print blahinstance.x
testclass() #blah will be incremented
print blahinstance.x #the incremented value holds after function exit
"------------------------------------------------------------------------------------"
x=5
def test():
x+=1
print x
print x
test() #fails because ref before assignemnt
So we have read access and modify access to global variables inside a local scope, but obviously attempts at re-assignment will just create a local variable of the same name as the global variable. In the examples above, what is different about referencing the instance attribute blahinstance.x which is outside of the functions scope? To me these examples are quite similar yet one fails and one does not. We do not have a ref before assignment error with blahinstance.x despite the fact that this object is in the global scope, similar to the second example of x.
To clarify - i totally understand the second example, and global vs local scope. What I don't understand is why the first works because it seems similar to the second. Is it because the instance object and it's attribute are mutable, and we have read/modify access to globals in a local scope?

Bare names are different from attribute references.
There is no name blahinstance.x. There is a name blahinstance, and the object it refers to has an attribute called x. When you do something like blahinstance.x += 2, the only variable you're referencing is blahinstance, and you're not assigning a new value to blahinstance, so all is well. The use of x in blahinstance.x is totally "internal" to blahinstance, and x is not really a variable at all, it's just an attribute name.
The "local variable referenced before assignment" business only comes into play when you assign to a variable --- that is, a bare name --- not an attribute reference, an item reference, or anything else. In this regard blahinstance.x += 2 is no different than somelist[1] += 2; x in the first case is no more a local variable than the index 1 is in the second case.

Related

Why it's impossible to reassign global name within local scope (without `global` keyword) with using the global name's value in reassignment? [duplicate]

This question already has answers here:
UnboundLocalError trying to use a variable (supposed to be global) that is (re)assigned (even after first use)
(14 answers)
Closed 5 months ago.
If we run this code
a = 1
def foo():
b = a + 2
print(b)
foo()
it works.
But if we run this code
a = 1
def foo():
b = a + 2
print(b)
a = a + 4
print(a)
foo()
it doesn't work.
Question:
why in the first example b = a + 2 works without errors but in the second example the same line of code b = a + 2 is broken?
why if there is no reassignment of a we can get a from a global scope but if there is a reassignment we don't have an access to a even if new local a doesn't even exist yet (because the reassignment isn't finished).
Question: Why [...] the first example works without errors but in the second example the same line of code is broken?
Because in the first example, you're only reading from a global a, then writing to a local b.
In the second example, a is assumed to be a local too (since you're not declaring global a), and it has no value when you're trying to read it in the first line, hence an error.
This is similar to JavaScript's Temporal Dead Zone, if that's familiar.
As for the question in your title:
Why it's impossible to reassign global name within local scope (without global keyword) with using the global name's value in reassignment?
You're not reassigning
a global name at all in either of your examples, since indeed you're not using the global keyword. In Python, you can always read a global (or "ambient") name; otherwise it'd be a pain to e.g. use any builtin function (since they're not locals), and having a special exception for builtins would be, well, a special exception, and those aren't nice.

Why is it that a function can modify global object attributes without an explicit global declaration? [duplicate]

This question already has answers here:
Global on mutable vs immutable
(1 answer)
Why is the global keyword not required in this case? [duplicate]
(1 answer)
Closed 8 months ago.
I am struggling to understand why a local function in Python 3 can seemingly modify global object attributes but not variables. If we consider object attributes to be variables attached to objects, this behavior appears very inconsistent and I cannot understand why the language behaves in this way.
We can create some class and use a function to modify an attribute of that class without anything like a global declaration of that attribute.
If we have some code:
class Integer:
def __init__(self, number):
self.value = number
n = Integer(20)
print(n.value) # display value at initialization
def increase_n():
n.value += 1
increase_n()
print(n.value) # display value after calling increase_n
Running this code would result in the following output:
20
21
We cannot do the same with a variable. An explicit global declaration must be used.
If we have some code:
k = 6
print(k) # display value after assignment
def increase_k():
global k
k += 2
increase_k()
print(k) # display value after calling increase_k
Running the above code results in:
6
8
Note that foregoing the global declaration for the second example would result in an error.
I am hoping someone can enlighten me. I am sure there must be some fundamental misunderstanding on my part. Why are variables and attributes treated so differently? Why is this good/bad?
The thing that the global keyword does is allow you to push a local variable (i.e. a name) assignment into the global (outermost) namespace. By default, a variable exists in the namespace of the scope in which you assign a value to it; global overrides that.
You can reference a variable from an outer scope without needing any special keyword; global is only significant for the assignment operation. Once you have a reference to the associated object, if it's mutable, you can freely mutate it, regardless of how you got that reference or where the object was created.
No global declaration is needed to affect the attribute n.value because it lives within the namespace of the object that n references, not the global namespace. The fact that n itself is a variable in the global namespace is immaterial; you could do:
def increase_n():
local_n = n # local_n is local, n is still global
local_n.value += 1
and it would have the same effect because local_n and n reference the same Integer object.

difference between += and append() in Python [duplicate]

This question already has answers here:
UnboundLocalError trying to use a variable (supposed to be global) that is (re)assigned (even after first use)
(14 answers)
Closed 1 year ago.
When I use the += in my function, I get this error: UnboundLocalError: local variable 'travel_log' referenced before assignment
but it totally works with the append() function.
What is the difference?
travel_log = []
def add_new_country(countries_visited, times_visited, cities_visited):
new_country = {}
new_country["country"] = countries_visited
travel_log += new_country
Because you assigned to travel_log in your add_new_country function (+= is an assignment), it is considered a local variable in that function. Because it is a local variable, and you never assigned it a value, travel_log has no value when you attempt to += to it. Python does not fall back to using the global variable of the same name.
This is somewhat surprising because the += operation on a list is equivalent to calling its extend() method, and the name remains bound to the same list. The error feels more reasonable when the variable is a number or some other immutable object. But it's the same behavior.
If you want to operate on the global variable, say so:
def add_new_country(countries_visited, times_visited, cities_visited):
global travel_log
# etc.
But it's better to use travel_log.append(). Appending isn't an assignment, so it doesn't make travel_log local.
This is 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. Since the last statement in add_new_country() assigns a new value to travel_log, the compiler recognizes it as a local variable.
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 local unless explicitly declared as global.
So in your case, declare travel_log as global scope inside the function
travel_log = []
def add_new_country(countries_visited, times_visited, cities_visited):
# now function will refer the globally declared variable
global travel_log
new_country = {}
new_country["country"] = countries_visited
travel_log += new_country
Checkout this blog for a detailed explanation: https://docs.python.org/3/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value

Python | if variable: | UnboundLocalError: local variable 'variable' referenced before assignment [duplicate]

This question already has answers here:
How can a name be "unbound" in Python? What code can cause an `UnboundLocalError`?
(3 answers)
Closed 15 days ago.
I am trying to use an if statement to check whether a variable has been assigned and is not None.
# Code that may or may not assign value to 'variable'
if variable:
do things
This throws an error at the line with the if statement: "UnboundLocalError: local variable 'variable' referenced before assignment".
I thought if the variable wasn't assigned it would just be interpreted as False?
I've tried if variable in locals(): to no avail.
What's going on? What can I do to achieve the result I'm looking for?
Thanks
It's better to simply initialize x to None at the beginning and test for None (as in Ned's answer).
What's going on is that whenever you reference a variable on a "load" (i.e. rvalue), Python looks it up and requires it to exist.
If your variable is named x, the following throws:
if x in locals(): ...
And is not what you wanted, since it would have checked if the value that the variable holds is in locals(). Not whether the name x is there.
But you can do
if 'x' in locals(): ...
At the beginning of the function initialize the variable to None. Then later you can check it with:
if x is not None:
Your assumption was wrong. There is no such thing as a uninitialized variable in Python. Any reference to a "variable" in Python either finds an entry in the namespace of some scope (Local, Enclosing, Global, or Builtin) or it's name lookup error.
It is better to think of Python statements of the form x=y as binding statements rather than assignments. A binding associates a name with an object. The object exists independently of its bindings (instantiated by a literal expression, for example). By contrast, most of the programming languages people are more familiar with have variables ... which have an associated type and location. Python "variables" are entries in a dictionary and the id() returns the the location of the object to which a name is bound. (The fact that this is a memory location is an implementation detail; but the principle is that Python "variable names" are not like the "variables" of more conventional languages).
Here's a bit more on that: https://softwareengineering.stackexchange.com/a/200123/11737
The best way to do accomplish this is simply refrain from using names before binding values thereto. Poking around in locals() or globals() for such things is just poor, ugly, coding practice.

UnboundLocalError in python confusing [duplicate]

This question already has answers here:
UnboundLocalError trying to use a variable (supposed to be global) that is (re)assigned (even after first use)
(14 answers)
Why does code like `str = str(...)` cause a TypeError, but only the second time?
(20 answers)
Closed 6 months ago.
Could anyone explain the exception the below code. It only works when I change the var sub in the display() to another name. There is no global variable sub as well. So what happened ?
def sub(a, b):
return a - b
def display():
sub = sub(2,1) // if change to sub1 or sth different to sub, it works
print sub
Any variable you assign to inside a scope is treated as a local variable (unless you declare it global, or, in python3, nonlocal), which means it is not looked up in the surrounding scopes.
A simplified example with the same error:
def a(): pass
def b(): a = a()
Now, consider the different scopes involved here:
The global namespace contains a and b.
The function a contains no local variables.
The function b contains an assignment to a - this means it is interpreted as a local variable and shadows the function a from the outer scope (in this case, the global scope). As a has not been defined inside of b before the call, it is an unbound local variable, hence the UnboundLocalError. This is exactly the same as if you had written this:
def b(): x = x()
The solution to this is simple: choose a different name for the result of the sub call.
It is important to note that the order of use and assignment makes no difference - the error would have still happened if you wrote the function like this:
def display():
value = sub(2,1) #UnboundLocalError here...
print value
sub = "someOtherValue" #because you assign a variable named `sub` here
This is because the list of local variables is generated when the python interpreter creates the function object.
This was originally a comment. The OP found this useful as an answer. Therefore, I am re-posting it as an answer
Initially, sub is a function. Then, it becomes the return value of a function. So when you say print sub, python doesn't know which sub you are referring to.
Edit:
First you define a function sub. Now, python knows what sub is.
When you create a variable and try to assign to it (say x = 2), python evaluates the stuff on the right hand side of the = and assigns the value of the evaluation as the value of the stuff on the left hand side of the =. Thus, everything on the right hand side should actually compute.
So if your statement was x = x+1, then x better have a value assigned to it before that line; and the previously defined x has to be of some type compatible with the addition of 1.
But suppose x is a function, and you make a variable called x in some other function, and try to assign to it, a value computed with function x, then this really starts to confuse python about which x you are referring to. This is really an oversimplification of this answer, which does a much better job of explaining variable scope and shadowing in python functions
For every variable used, Python determines whether it is a local or a nonlocal variable. Referencing a unknown variable marks it as nonlocal. Reusing the same name as a local variable later is considered a programmers mistake.
Consider this example:
def err():
print x # this line references x
x = 3 # this line creates a local variable x
err()
This gives you
Traceback (most recent call last):
File "asd.py", line 5, in <module>
err()
File "asd.py", line 2, in err
print x # this line references x
UnboundLocalError: local variable 'x' referenced before assignment
What happens is basically that Python keeps track of all references to names in code. When it reads the line print x Python knows that x is a variable from a outer scope (upvalue or global). However, in x = 3 the x is used as a local variable. As this is a inconsistency in the code, Python raises an UnboundLocalError to get the Programmers attention.
Python start executing your code and get the function first
def sub(a, b):
return a - b
So after executing this interpreter get the sub as a function. Now when come to next line it found
def display():
sub = sub(2,1) // if change to sub1 or sth different to sub, it works
print sub
so first line sub = sub (2, 1) will convert the sub function to sub variable. From this function you are returning the sub variable. So its create problem.

Categories

Resources