10

Why does this code work:

var = 0

def func(num):
    print num
    var = 1
    if num != 0:
        func(num-1)

func(10)

but this one gives a "local variable 'var' referenced before assignment" error:

var = 0

def func(num):
    print num
    var = var
    if num != 0:
        func(num-1)

func(10)
JJ Beck
  • 5,193
  • 7
  • 32
  • 36

6 Answers6

9

Because in the first code, you have created a local variable var and used its value, whereas in the 2nd code, you are using the local variable var, without defining it.

So, if you want to make your 2nd function work, you need to declare : -

global var

in the function before using var.

def func(num):
    print num
    var = 1  <--  # You create a local variable
    if num != 0:
        func(num-1)

Whereas in this code:

def func(num):
    print num
    var = var <--- # You are using the local variable on RHS without defining it
    if num != 0:
        func(num-1)

UPDATE: -

However, as per @Tim's comment, you should not use a global variable inside your functions. Rather deifine your variable before using it, to use it in local scope. Generally, you should try to limit the scope of your variables to local, and even in local namespace limit the scope of local variables, because that way your code will be easier to understand.

The more you increase the scope of your variables, the more are the chances of getting it used by the outside source, where it is not needed to be used.

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • 1
    or, better yet, don't use global variables in the first place :) – Tim Pietzcker Oct 26 '12 at 17:12
  • @TimPietzcker. Ok. Can you please specify a specific reason for that? – Rohit Jain Oct 26 '12 at 17:14
  • No, he's using the local variable on the RHS in the second example, which is why it's an error; it's not defined yet. – Wooble Oct 26 '12 at 17:14
  • @RohitJain: Some reasons not to use global variables: http://c2.com/cgi/wiki?GlobalVariablesAreBad (but it's worth noting that in Python, your top-level functions and classes are actually global variables, which is an example of why you don't want that as a hard and fast rule, but as a heuristic). – abarnert Oct 26 '12 at 17:19
  • @abarnert. Thanks :) Will take a look at the link :) – Rohit Jain Oct 26 '12 at 17:23
  • @abarnert. Ok, so this is not particularly for python. These problems prevail in general programming, where we should limit the scope of varables to `local` to make life easier right? – Rohit Jain Oct 26 '12 at 17:24
  • @RohitJain: Yes, it's general for most languages, including but not limited to Python. There are also essays online explaining why globals are bad specifically in Python, but essentially they repeat most of the same arguments, plus the extra Python-specific quirk that's behind the OP's question. – abarnert Oct 26 '12 at 17:40
  • @abarnert. Yeah as Python has a different way to use global variables. Thanks again for the link. Really helpful. I have saved it as bookmark :) – Rohit Jain Oct 26 '12 at 17:41
  • @RohitJain: For historical reasons, you may also want to read the famous article "Global Variables Considered Harmful" (or at least the page on it at http://c2.com/cgi/wiki?GlobalVariablesConsideredHarmful). However, nearly everyone nowadays disagrees with half of the central arguments and most of the side points, which is why WardsWiki has that separate "AreBad" page. – abarnert Oct 26 '12 at 17:50
  • @abarnert. Ahh! thanks for that too. :) I would read both of them. – Rohit Jain Oct 26 '12 at 17:53
6

If you have var = ... anywhere in a function, the name "var" will be treated as a local variable for the entire function, regardless of where that assignment occurs. This means that all occurrences of var in your function will be resolved in the local scope, so the right hand side of var = var results in the referenced before assignment error because var has not yet been initialized in the function's scope.

Andrew Clark
  • 202,379
  • 35
  • 273
  • 306
  • This surprised me, too. I would have assumed that `func` would be a closure over `var`, and that the `var = var` line would have created a function-local variable, `var`, and assigned it the value of the module-level `var`. The "anywhere in a function" part of your answer smells magical to me. Surely that's not the actual cause? – Kirk Strauser Oct 26 '12 at 17:31
  • 1
    @KirkStrauser: it is somewhat magical. The scope of names is static, unlike just about everything else. – Wooble Oct 26 '12 at 17:40
  • 1
    @KirkStrauser: Python doesn't really have variables in the sense you're thinking of; it has name bindings. For most simple cases, the effect is the same, but it does mean that some cases are confusing until you understand the difference. In particular, if you've learned the rules for "how closures work in dynamic languages" from Javascript, you're going to be thrown off by Python. – abarnert Oct 26 '12 at 17:43
  • @abarnert Actually, I learned that all in Python. I'm familiar with name bindings, "pass by object reference", etc. It's just that I hadn't seen this case before and it isn't as I would have expected. – Kirk Strauser Oct 26 '12 at 19:32
  • Now that I've read a little more, to expand on F.J's answer: variables are added to a function's list of local variables (in the `co_varnames` attribute) at compilation time if they are used on the left-hand side of an assignment. This leads to (to me!) interesting situations like `def foo(): var = var` failing but `def foo(): exec('var = var')` being perfectly fine. I'd describe this as more of an implementation detail of an optimization pass than a feature of the language itself. – Kirk Strauser Oct 26 '12 at 19:46
  • Well, it's not an implementation detail, because any valid implementation of Python (including pypy, Jython, and IronPython) must define all local variables in the scope in which they're defined. With the `exec`, the variable doesn't exist at compile time (or, alternative, its "compile time" is when the `exec` runs, rather than when `foo` is defined). You can also "get around this" in a similar sense by `def foo(): locals()['var'] = var`, but it still doesn't invalidate the rule. – abarnert Oct 27 '12 at 00:13
1

You can read a global without declaring it global. But to write a global, you need to declare it global.

user1277476
  • 2,871
  • 12
  • 10
  • This isn't the OP's problem. True, his first function may not do what he hoped, but he's asking why the second function raises an exception. And "You can read a global without declaring it global" is exactly what's confusing him: he _can't_ read the global `var` without declaring it global in this case, because he's got a local name that overshadows it. – abarnert Oct 26 '12 at 17:55
0

In your second piece of code you have created a local variable in RHS and without defining it, you are assigning it to the LHS variable var which is global (a variable defined outside the function is considered global explicitly).

If your intention is to create a local variable inside the function and assign it to the value of the global variable, this will do the trick:

var = 0

def func(num):
    print num
    func.var = var # func.var is referring the local
                   # variable var inside the function func
    if num != 0:
        func(num-1)

func(10)
fenceop
  • 1,439
  • 3
  • 18
  • 29
TechnoTech
  • 744
  • 4
  • 14
-1
def runscan(self):
    p = os.popen('LD_PRELOAD=/usr/libv4l/v4l1compat.so zbarcam
                /dev/video0','r')

def input(self):
    self.entryc.insert(END, code)

how about this? i want use local 'code' to the next function to insert the result of barcode to my Tkinter entryBox.. Thanks

  • This looks like you're asking a new/unrelated question to the original post. Consider posting this as a new question. – Jake Jan 06 '17 at 19:36
-3

Each function block is a local scope. If you want to assign to global variables, you need to do so explicitly:

var = 0

def func(num):
    print num
    global var
    var = 1
    if num != 0:
        func(num-1)

print var # 0
func(2)
print var # 1
Nisan.H
  • 6,032
  • 2
  • 26
  • 26
  • 1
    This implies that the OP's first example shouldn't work either, and yet it does. Plus, your example doesn't even use the global var. – abarnert Oct 26 '12 at 17:20
  • I don't even know what you're reading at this point. The OP's first example does not throw an exception—in fact, that's the whole reason he came here asking this question. – abarnert Oct 26 '12 at 17:53
  • The question was about the second example, not the first. There's no reason for the first to throw any exceptions... @RohitJain explained why the second example throws an exception. I'm still not sure why you think this answer implies that the first example shouldn't work. – Nisan.H Oct 26 '12 at 17:56
  • It feels like you're trying to score points, rather than give useful information. You've removed one comment and edited your answer in ways that make my comments seem senseless, but your answer still doesn't address the OP's confusion at all, or explain anything that's missing from the two existing useful answers. I'm done commenting here. – abarnert Oct 26 '12 at 18:10
  • The OP's purpose in the second function is ambiguous: he could be trying to read the global var, or he could be trying to set it, or both. Since he didn't provide any additional input since asking the question, neither of us know for sure what he was trying to achieve. I assumed the case of trying to set the global var, and you assumed the other. I edited the answer to make it clearer, and removed my first comment because it wasn't constructive. And by the way, this entire comment thread isn't very constructive either... So lets just stop. – Nisan.H Oct 26 '12 at 18:33