Function Scope and Argument Types. They solve a deceptively simple problem: how does Python decide which variable names a function can see, and how does a caller hand data into a function without causing chaos? For the PCEP-30-02 exam you need to understand that variables are not just floating around — they live in specific neighbourhoods (scopes) and arguments arrive at functions like addressed packages, either with a label (keyword) or by position order.
Jump to a section
A simple way to picture Function Scope and Argument Types
Have you ever watched a cook in a bustling restaurant kitchen, calmly pulling ingredients from a labeled drawer while the chef at the next station frantically searches the whole room for salt?
That is exactly the difference between local scope and global scope in Python. In the kitchen, each cook has their own small drawer right at their station. That drawer is the local scope. It holds the spices and tools that cook needs for their current dish. Other cooks cannot just grab from that drawer because it belongs to that station. When a function runs in Python, it creates its own little drawer of variable names. Those variable names only exist inside that function. They are local. If a different function tries to use that same name, it will get a NameError, just like the chef from the next station reaching into your drawer and finding nothing.
But there is also the pantry. The pantry is global scope. Any cook can grab a jar of salt from the pantry. Global variables in Python are like that pantry salt — they are accessible to every function in the program. But a good cook rarely uses the pantry for their main ingredients because it is too far and everyone fights over the same jar. Imagine the chaos if every function changed the same global variable! That would be like one cook turning the pantry's salt into sugar without telling anyone. Scope keeps things organised, safe, and predictable.
Let us start with the most important idea: scope is the region of your program where a variable name is visible and can be used. Python follows a rule called LEGB (Local, Enclosing, Global, Built-in). For the PCEP-30-02 exam, you mainly need to understand Local and Global scopes.
When you write a variable inside a function, that variable is born when the function runs and it dies when the function ends. That is local scope. If you define a variable at the top level of your script, outside any function, it has global scope. That variable lives for the entire run of the program.
Here is a classic trap. You cannot modify a global variable inside a function without using the 'global' keyword. If you try to assign a value to a variable name inside a function, Python assumes you are creating a new local variable, even if a global variable with the same name exists outside. This causes a UnboundLocalError if you try to read the variable before you assign it, because the local variable is not yet defined.
Now let us talk about how arguments get into functions. There are two types you must memorise for the exam: positional arguments and keyword arguments. - Positional arguments: the order matters. The first value you pass goes into the first parameter, the second into the second parameter, and so on. If you have a function 'def greet(name, greeting)', calling 'greet("Alice", "Hello")' puts "Alice" into name and "Hello" into greeting. Mix up the order and you get unexpected results. - Keyword arguments: you specify the parameter name explicitly. Calling 'greet(greeting="Hi", name="Bob")' works even though the order is swapped, because you used the keyword. The Python interpreter matches the keyword to the parameter name.
You can mix both types, but there is a hard rule: all positional arguments must come before any keyword arguments. If you write 'greet("Charlie", greeting="Hey")', that works. But 'greet(name="David", "Good morning")' will cause a SyntaxError because "Good morning" is a positional argument that comes after a keyword argument.
Why does this matter? Because functions are how you package reusable logic. Without a clear way to pass data in (arguments) and a clear rule for which variables the function can see (scope), large programs would be impossible to debug. The scope rule protects you from accidentally changing a variable that another part of your program relies on. Argument types let you write flexible functions that can accept inputs in a readable order.
There is one more nuance: default parameter values. You can define a parameter with a default value, like 'def power(base, exponent=2)'. If the caller omits the exponent, it defaults to 2. Default parameters are evaluated only once, when the function is defined — not each time it is called. That matters for mutable defaults like lists, but that is beyond PCEP-30-02. For now, just know that default values are assigned at definition time.
Finally, remember that changing a mutable object passed as an argument (like a list) inside a function will affect the original list outside. But reassigning the parameter name to a new object will not. This is often called 'pass by object reference'. Python does not pass a copy; it passes a reference to the actual object. If you modify the object through the reference, the caller sees the change. If you assign a new object to the parameter, the reference is lost locally and the original outside stays intact.
Define a function with parameters
Write 'def my_function(param1, param2):'. The parameters are local variable names that will hold the incoming argument values. They are only visible inside the function body.
Call the function with positional arguments
Write 'my_function(10, 20)'. Python assigns 10 to param1 and 20 to param2 purely by position. If you swap them, param1 gets 20. This is the simplest calling method.
Call the function with keyword arguments
Write 'my_function(param2=20, param1=10)'. The order no longer matters because Python uses the parameter names written after the equals sign to match the values. This improves readability for functions with many parameters.
Mix positional and keyword arguments
Write 'my_function(10, param2=20)'. The positional argument (10) is first and matches param1. The keyword argument (param2=20) is second. This is valid because all positional arguments come before all keyword arguments.
Use a global variable inside the function
Write 'global x' at the top of the function body, then assign to 'x'. Without that line, 'x = 5' inside the function would create a new local variable named x, leaving the global x unchanged. The global statement tells Python to use the global scope for that name.
Imagine you work in a small e-commerce company's IT support team. Your manager asks you to write a Python script that calculates discounts for customer orders. This is a perfect scenario to apply function scope and argument types in a realistic way.
You start with a global variable 'TAX_RATE = 0.08' because the tax rate applies to every function. You also have a global list 'order_history' that logs every discount applied.
Your first task is to write a function 'calculate_discount(order_total, customer_tier)'. This function needs to accept the order total as a positional argument and the customer tier (like 'gold' or 'silver') as a keyword argument because you want to make the tier optional and default to 'bronze'. Inside the function, you check the customer_tier and calculate the discount. You also need to append a record of this discount to the global 'order_history' list. Since you are modifying the list (not reassigning it), you do not need the 'global' keyword to append to 'order_history'. But if you accidentally tried 'order_history = []' inside the function, you would be creating a new local variable, not clearing the global one. That would be a bug.
Now your manager introduces a new requirement: they want a function that logs a message every time a certain condition is met, but they realise they accidentally used a common variable name 'log_count' both at the global level and inside the function. Without understanding scope, you would cause a UnboundLocalError because the function tries to increment 'log_count' but Python sees a local assignment and does not look for the global. The fix is to either rename one or use 'global log_count' inside the function.
Later, you are asked to write a function that formats a shipping label. The function 'create_label(first_name, last_name, city, country="USA")' is called many times with positional arguments for the customer names and a keyword argument only when the country differs. You can call 'create_label("Jane", "Doe", "London", country="UK")' and it works perfectly. But if someone calls 'create_label(city="Paris", "Jean", "Dupont")', Python throws a SyntaxError because the positional argument comes after the keyword argument. Your job as a developer is to write documentation and possibly add input validation to prevent such errors.
In practice, you will also use the 'global' keyword sparingly. Experienced developers treat global variables like a shared phonebook — convenient but easy to overwrite by accident. For the exam and real work, prefer passing values as arguments and returning results. This makes your functions pure and testable. When you debug a script with scope issues, you literally step through the code line by line with print statements to see which variable the function sees. That is how IT professionals spend their time: not memorising syntax, but tracing where a variable comes from.
The PCEP-30-02 exam tests function scope and argument types directly in at least 4-6 questions across the exam. You absolutely must know the following.
First, the LEGB rule. Questions will show you a nested function and ask what value a variable will have. Practise reading code from the inside out. For PCEP, you only need to master Local and Global. Enclosing (closure) and Built-in are tested lightly but understanding the order is crucial.
Second, the difference between modifying a mutable object and reassigning a variable inside a function. They love this trap. They give you a function that modifies a list, and then print the list outside. The answer is that the list changed. Then they give you a function that does 'x = 5' inside, and print 'x' outside, and the answer is whatever the global x was. Memorise this pattern.
Third, keyword-only arguments. You must know that you cannot use a positional argument after a keyword argument. The exam will show a code snippet with mixed argument types and ask whether it is valid. The invalid ones always violate the positional-before-keyword rule.
Fourth, default parameter values. They will write a function with a default parameter and call it with zero arguments. You must know that the default value is used. But they may also test that default values are evaluated only once at definition time (though this is more of a PCPP-30-02 topic, PCEP might touch it lightly).
Fifth, the 'global' keyword. They will show a function that tries to modify a global variable without using 'global' and ask what happens. The correct answer is a UnboundLocalError. They will also show a function that uses 'global x' and then modifies x, and ask what the global value is after the function call.
Here is the list of exact concepts and traps:
Local vs global scope: variable visibility
UnboundLocalError: reading a local variable before assignment when a global variable with the same name exists
'global' statement: when to use it
Positional arguments: order matters
Keyword arguments: named params, order independent
Mixing positional and keyword: positional must come first
Default parameter values: assigned at definition
Mutable argument modifications: changes persist outside the function
The trap patterns are: they always put a print statement after a function that modifies a global via side effect (list append), and they expect you to notice the change. They also love to put a variable name inside a function that shadows a global, then the function tries to read it before assigning it — that is an immediate UnboundLocalError.
To memorise: 'Every variable has a scope. If you assign to it inside a function, it becomes local unless you say global. Arguments are passed by object reference. Mutable objects can be changed inside the function. Order of arguments: positional, then keyword.'
A variable assigned inside a function is local by default and cannot be seen outside that function.
To modify a global variable inside a function, you must declare it with the 'global' keyword first.
Positional arguments are matched to parameters by order; keyword arguments are matched by name.
In a function call, all positional arguments must appear before any keyword arguments.
Default parameter values are set once at function definition time, not each time the function is called.
If a mutable object like a list is passed as an argument and modified inside the function, the change persists outside the function.
These come up on the exam all the time. Here's how to tell them apart.
Local Variable
Defined inside a function body
Only exists while the function runs
Cannot be accessed outside the function
Global Variable
Defined at the top level of a script
Exists for the entire program run
Accessible from any function (with or without 'global' keyword to modify)
Positional Argument
Matched to parameters by position in the call
Order must match the function definition
Cannot be used after a keyword argument in the same call
Keyword Argument
Matched to parameters by parameter name
Order does not matter
Must appear after all positional arguments in the call
Modifying a Mutable Argument (e.g., list)
Changes the original object outside the function
No 'global' keyword needed for this operation
Example: my_list.append(5)
Reassigning a Parameter
Does not affect the original variable outside
Creates a new local binding for the parameter name
Example: my_list = [1,2,3] inside the function
Mistake
If I pass a variable to a function, the function gets a copy, so changing it inside the function won't affect the original.
Correct
Python passes a reference to the object, not a copy. If the object is mutable (like a list), changes inside the function affect the original. If you reassign the parameter name, the original variable is untouched.
People often carry the 'pass by value' mental model from other languages (or assume it works like that). The exam tests this by having a function append to a list and then checking the list outside.
Mistake
Using the same variable name inside a function automatically refers to the global variable.
Correct
If you assign a value to a variable name inside a function, Python creates a new local variable. To modify the global variable, you must use the 'global' keyword.
It seems intuitive that a variable with the same name would be the same variable. But Python's design chooses safety: local names are always local by default unless explicitly stated.
Mistake
Keyword arguments can be placed anywhere in the function call, including before positional arguments.
Correct
All positional arguments must come before any keyword arguments in a function call. Putting a keyword argument first causes a SyntaxError.
Python's grammar is strict here. Beginners often think that because keyword arguments are named, order shouldn't matter at all. But the interpreter cannot know where the positional arguments end if keywords interrupt the sequence.
Mistake
Default values are evaluated fresh each time the function is called.
Correct
Default values are evaluated only once, when the function is defined, not each time it is called. For immutable defaults like numbers and strings, this makes no practical difference. For mutable defaults like lists, it causes surprising behaviour.
The phrase 'default value' implies the value is set each time you call the function. But Python attaches the default to the function object at definition time. This is counterintuitive and a favourite exam trick.
Mistake
If a function changes a global list using 'append', I need the 'global' keyword.
Correct
The 'global' keyword is only needed when you are reassigning the variable name itself (e.g., 'my_list = []'). Modifying the object that the name refers to (like appending to a list) does not require 'global' because you are not changing what the name points to.
Beginners think 'global' controls all access to a global variable. In reality, it only controls assignment to the name. Reading and mutating the object are allowed without it.
Reveal each answer, then mark whether you got it right. Score 60%+ to unlock the next chapter.
Local scope refers to variables defined inside a function — they only exist while the function runs. Global scope refers to variables defined at the top level of a script, outside any function, and they are accessible everywhere. A function can read a global variable but cannot change its value without using the 'global' keyword.
You get UnboundLocalError because Python sees you assigning to a variable name inside the function and therefore treats it as a local variable. If you then try to read that same name before assigning a value to it, Python raises UnboundLocalError. To fix it, either don't assign to the name or use the 'global' keyword.
No. Python's syntax requires that all positional arguments appear before any keyword arguments in a function call. If you put a keyword argument first, you will get a SyntaxError. This is a strict rule you must remember for the exam.
Almost never. Global variables make code harder to debug because any part of the program can change them. Use arguments to pass data into functions and return values to get data out. The only common exception is for constants (like a tax rate or a configuration value) that are truly read-only.
Because Python passes the reference to the object, not a copy of the object. When you call a function with a list argument, the parameter holds the same reference. If you modify the list (e.g., with append), you are modifying the same object that exists outside the function. If you reassign the parameter to a new list, the original list is unaffected.
LEGB stands for Local, Enclosing, Global, Built-in. It is the order Python uses to look up variable names. It first checks the local scope of the current function, then the enclosing scopes of any outer functions, then the global scope, and finally the built-in scope. For the PCEP-30-02 exam, you primarily need to understand Local and Global.
You've finished Function Scope and Argument Types. Continue through the PCEP-30-02 study guide to build a complete picture of the exam.
Done with this chapter?