Python gotchas compilation

By Niraj Zade  |  2023 Sep 17  |  5m read  |  1058 words

Variable scope leakage, global variable read-write issues

This is an ever-growing note.

But the big ones that will trip up polygots are: 
1. Variable scope "leakage"
2. Python assignment model

Variable scope "leakage"

Compared to most other languages, python's variable scopes are very weird. If you are coming from other languages like java, c#, c++ etc, then this is going to lead to unintentional bugs.

Introduction

Does the following program throw an error? Or does it print hello?

if True:
    var = "hello"
if True:
    print(var)

The answer is: It prints hello.

If this caught you by surprise, then this note is for you. If you are coming from other languages like java, c#, c++ etc, then this is something that is different in python. It can trip you up especially if you're a polygot.

There are 2 major reasons why polygots cause bugs in python projects:

  1. Not understanding scope of variables in python
  2. Not understanding the python assignment model

This article focuses on point 1 - Not understanding scope of variables in python.


You don't need to declare variable in the outer scope before setting its value or accessing it:

is_available = None # no need to decalre the variable here
if(<your condition>):
    is_available = True
else:
    is_available = False

print(is_available)

Instead, you can just do this:

if(<your_condition>):
    is_available = True
else:
    is_available = False

print(is_available) # variable is still accessible here!

In python, the variables "leak" into the outer scopes.

Note: for mutual sanity, I recommend declaring variable first in the outermost possible scope, before you go ahead and use it. Keeps things easier to reason with.

Explanation

We can put it in one line:

**Variables are scoped to the nearest function, class, module.

In this exact order.**

Now, again let's look at an example. What's the output of the program below?

def fn():
    if True:
        variable_in_if =1234
        print("Value inside if is:", variable_in_if)
    # still accessible, outside the if
    print("Value outside if is:", variable_in_if)

fn()

Again, coming from other languages, you may expect the output to be:

Value inside if is: 1234
Value outside if is: None (or maybe some error at this live)

But in python, the actual output is:

Value inside if is: 1234
Value outside if is: 1234

The variable_in_if is accessible outside the if also, as if it leaked out.

Now we know for sure - variables "leak" out in python. This isn't an anomaly. This is a normal behaviour in python.

The question you should be asking is: To what extent does a variable leak? What is the scope of this leak?

Answer - Variables are scoped to the nearest function, class, module. In this exact order.

In the above example, the variable_in_if was scoped to the nearest function, which was example_function(). So once it got declared and assigned a value, it became accessible from everywhere throughout the function.


Here is another example. This time, the if that declares the variable is nested inside another if:

def example_function():
    if True:
        if True: # nested if
            var = "hello"
    print(var) # var is still accissible outside the nested if

example_function() # prints hello

The scope of the variable var was set to the nearest function - example_function(). It is accessible throughout the function. Doesn't matter how much nesting is done, the variable will leak into the scope of example_function().

Examples to drill it down

Scope within a function

def outer_function():
    variable_of_outer_function = 1234
    def inner_function():
        print(variable_of_outer_function)
        return
    return

Here, variable_of_outer_function is scoped to the nearest function, which is outer_function. So it is available everywhere inside the outer function. So it is also accessible inside inner_function().

Scope in nested functions

def outer_function():
    def inner_function():
        variable_of_inner_function = 1234
        return
        print(variable_of_inner_function) # accessible within inner_function
    print(variable_of_inner_function) # NOT accessible in outer_function
    return

Here, variable_of_inner_function is scoped to the nearest parent function inner_function. So it is not accessible to the print statement of outer_function.

Global vs local scope collision

variable = 1234
def the_function():
  variable = 6789
  print(variable)

In this case there is a global variable and a local variable within the function.

Here, python treats these two as different variables, and preserves the value of both of the. The local variable does not override the value of the global variable.

So, when python tries to fetch the value of variable to print(), it gets 2 possible values: global and local. In this case, python will: Look for the variable in the current scope. If variable isn't found in current scope, it will go one scope higher and look for the variable. It will keep going higher and higher in scope until it finds a variable of the same name.

If it finds the variable, it will print that value. If it doesn't find any variable, it will raise an exception.

In this case, python is finding value for the print() in the function's local scope, and will find variable right there in the function's local scope. So it will use that local scope value and print 6789 . It didn't have to go up to a higher scope to find the variable.

In this example below, it won't find variable in the function's scope. So it will go up and find variable in the global scope. So python will print 1234, which is the global scope's variable's value.

variable = 1234
def the_function():
  print(variable)

Can read a global without global keyword, but can't write to it

You can read global without using the global keyword (if a local variable doesn't override it).

However, you cannot write to a global without using the global keyword.

GLOBAL_VAR = 1

def fn():
    print(GLOBAL_VAR)
fn()

def fn_read_global():
    print(GLOBAL_VAR)
fn_read_global() # Works fine. prints 1

def fn_modify_global():
    GLOBAL_VAR += 1 # <- UnboundLocalError: local variable 'GLOBAL_VAR' referenced before assignment
    print(GLOBAL_VAR)
fn_modify_global() # <- UnboundLocalError: local variable 'GLOBAL_VAR' referenced before assignment
# why? because fn_modify_global tried to modify GLOBAL_VAR in it's own restricted "fn_modify_global" namespace

Solution: tell the interpreter to use GLOBAL_VAR from the global namespace

# 
def fn_modify_global_corrected():
    global GLOBAL_VAR # declare that GLOBAL_VAR is from the global namespace
    GLOBAL_VAR += 1
    print(GLOBAL_VAR)
fn_modify_global_corrected() #<- Works fine, prints 2

Blog posts

[2025 Jul 16] My subconscious doesn't like LLMs ( 10m to read | (1969 words )

[2023 Apr 08] Computers understanding humans makes codebases irrelevant ( 6m to read | (1267 words )

[2023 Feb 12] Own your email's domain ( 5m to read | (934 words )

[2023 Jan 03] Isolates + storage over http + orchestrators is the future that has arrived ( 5m to read | (1072 words )

[2023 Jan 03] Why interpreted languages make superior scripting languages ( 1m to read | (287 words )


Articles

I learn through writing. Most of these are ever evolving pieces.


python

Python gotchas compilation
2023 Sep 17 | 5m (1058 words)

Unicode string normalization schemes in Python
2024 May 06 | 7m (1303 words)


resources

Catalyzing research within India
2025 Aug 25 | 1m (239 words)

Links collection
2025 Jun 15 | 3m (612 words)

Papers, books, talks etc
2025 Jun 02 | 4m (744 words)


spark

Delta Lake - performance optimization and maintenance
2026 Jan 03 | 13m (2496 words)

Spark Microbook
2025 Oct 16 | 26m (4773 words)

Spark join strategies
2024 Jan 22 | 13m (2450 words)

Spark performance optimization compendium
2025 Oct 11 | 4m (887 words)


sqlserver

SqlServer reference - All date & time formatters
2025 Jul 10 | 2m (519 words)


work

Lecture - You and your research by Dr. Richard Hamming
2024 Oct 14 | 1hr18m (14441 words)

Why charge more as an engineer
2025 Oct 13 | 6m (1253 words)

© Niraj Zade 2025 - Website, Linkedin
Website was autogenerated on 19 Jan 2026
Whoever owns storage, owns computing