Introduction
In this lesson, you will deepen your understanding of how Python manages variables at runtime. You already know how to declare variables and convert types; now we’ll explore dynamic typing and how Python decides what type a variable has while the program is running. You’ll also learn how variable scope works—where variables are visible, how long they live, and how to control them using functions and the global keyword.
By the end of this lesson, you’ll be able to reason about where your data exists in a program and what type it has at any point in time.
1. Python’s Dynamic Typing
Python is a dynamically typed language. This means:
- You don’t declare variable types explicitly (no
int xorstring name) - The type is attached to the value, not the variable name
- A single variable name can refer to values of different types over time
Example: Types Determined at Runtime
value = 10 # currently refers to an int
print(value, type(value))
value = 3.14 # now refers to a float
print(value, type(value))
value = "Python" # now refers to a str
print(value, type(value))
# Output
10 <class 'int'>
3.14 <class 'float'>
Python <class 'str'>
Here, value is just a label. Python binds that label to an integer, then to a float, then to a string at runtime. This flexibility is powerful, but it also means you must be disciplined about how you use variables so your code remains clear and predictable.
2. Inspecting Types with type()
The built‑in type() function lets you inspect the type of a value while your program is running. This is especially helpful when:
- Debugging mixed data
- Handling user input that may come in different formats
- Verifying assumptions in complex logic
Example: Checking Types Dynamically
user_input = "123"
print("user_input:", user_input, type(user_input))
number = int(user_input)
print("number:", number, type(number))
is_large = number > 100
print("is_large:", is_large, type(is_large))
# Output
user_input: 123 <class 'str'>
number: 123 <class 'int'>
is_large: True <class 'bool'>
Even though user_input looks numeric, type() confirms it starts as a string, then becomes an integer, then is involved in an expression that produces a boolean.
3. Variable Reassignment with Different Data Types
Because of dynamic typing, reassigning a variable to a new value of a different type is trivial—but it has consequences for code readability and correctness.
Example: Reassignment and Its Implications
item = "5" # item is a string
print("item:", item, type(item))
# Later in the code
item = int(item) + 10 # item is converted and reassigned as an int
print("item:", item, type(item))
# Even later
item = [item, item * 2] # item is now a list
print("item:", item, type(item))
# Output
item: 5 <class 'str'>
item: 15 <class 'int'>
item: [15, 30] <class 'list'>
Reusing item for unrelated types can make code confusing. Practically, it’s better to use clear, type‑specific names, for example item_str, item_int, or item_values. Dynamic typing gives you freedom, but good naming and consistent use keep your programs understandable.
4. Understanding Variable Scope in Python
Scope defines where in your code a variable is visible and usable. Python follows the LEGB rule for resolving names:
- Local – inside the current function
- Enclosing – inside any enclosing functions (for nested functions)
- Global – at the module (file) level
- Built‑in – names provided by Python itself (like
len,print,type)
In this lesson, we’ll focus on local, global, and built‑in scopes.
4.1 Local Scope (Inside Functions)
Variables defined inside a function are local to that function.
message = "global message"
def show_messages():
message = "local message" # local variable
print("Inside function:", message)
show_messages()
print("Outside function:", message)
# Output
Inside function: local message
Outside function: global message
The message inside show_messages is a separate, local variable that shadows the global message only within the function.
4.2 Global Scope (Module-Level Variables)
Variables defined at the top level of a file (not inside any function/class) are in global scope for that module.
counter = 0 # global variable
def increment():
local_counter = 5
print("Inside increment, local_counter =", local_counter)
increment()
print("Outside function, counter =", counter)
# Output
Inside increment, local_counter = 5
Outside function, counter = 0
local_counter exists only inside increment. counter is available everywhere in the same file, except when hidden by a local variable of the same name.
4.3 Built-in Scope
Python provides many names in the built‑in scope, e.g., len, print, type, range. You can use them anywhere without importing.
Be careful not to override built‑in names unintentionally:
len = 10 # Avoid doing this!
# Later in the code
my_list = [1, 2, 3]
# print(len(my_list)) # This would now fail, because len is no longer a function
Overwriting built‑ins can cause confusing errors. Use more descriptive variable names instead.
5. Controlling Visibility and Lifetime with global
Normally, assigning to a variable inside a function creates a new local variable. If you want to modify a global variable from inside a function, you must use the global keyword.
Example: Modifying a Global Variable
total_visits = 0 # global variable
def record_visit():
global total_visits # tell Python we mean the global variable
total_visits += 1
print("Inside function, total_visits =", total_visits)
record_visit()
record_visit()
print("Outside function, total_visits =", total_visits)
# Output
Inside function, total_visits = 1
Inside function, total_visits = 2
Outside function, total_visits = 2
Without global total_visits, the line total_visits += 1 would try to create and update a local variable, leading to an UnboundLocalError.
When to Use (and Avoid) global
- Use
globalsparingly; it can make code harder to reason about - Prefer returning values from functions and updating state explicitly
- Globals are reasonable for very simple scripts or truly shared configuration, but larger programs usually encapsulate state in functions, classes, or data structures
Example: Safer Alternative Without global
def record_visit(current_total):
current_total += 1
return current_total
total_visits = 0
total_visits = record_visit(total_visits)
print("After 1st visit:", total_visits)
total_visits = record_visit(total_visits)
print("After 2nd visit:", total_visits)
# Output
After 1st visit: 1
After 2nd visit: 2
Here, the function is pure: it doesn’t depend on or modify any global variables. This makes the code more predictable and easier to test.
Summary / Key Takeaways
In this lesson on Dynamic Typing and Variable Scope Basics, you have learned:
- Python uses dynamic typing, where types are associated with values at runtime, and variable names can be rebound to different types over the life of a program.
- The
type()function lets you inspect data types during execution, helping with debugging and verifying assumptions about variable contents. - Variables can be reassigned to different data types, which is legal but should be managed carefully to preserve code clarity and maintainability.
- Python resolves names using scope rules, primarily distinguishing between local, global, and built‑in scopes according to the LEGB lookup order.
- Local variables exist only inside functions, can shadow globals of the same name, and are not accessible outside their defining function.
- Global variables live at module level and are accessible throughout that module unless shadowed by locals, but overuse can lead to tightly coupled code.
- Built‑in names like
len,print, andtypeare always available, and accidentally redefining them can break expected functionality. - The
globalkeyword allows functions to modify global variables, but a better practice in many cases is to pass values into functions and return updated results instead of mutating global state directly.
