:
the def keyword, docstring, arguments, and return.
Typed Hints
:
defined with type hints (soft suggestions) for arguments and return, enhancing readability and allowing static analysis.
Typed Checks
:
defined with type checks (hard rules) for arguments and return, via the typeguard module and @typechecked decorator.
Arguments
Positional, Keywords
: positional args depend on the order, while
keyword args are identified by the parameter name.
Default Values
: parameters with predefined values that are used if
no argument is provided during the call.
Variadic
: *args collects extra positional args into a
tuple, and **kwargs collects extra keyword args into a dictionary.
Unpacking
: using * or ** to explode a
sequence (list/tuple) or a dictionary into individual function arguments.
Keyword-Only
: forcing specific arguments to be passed only by name
using a standalone * in the parameter list.
Signature Order
:
Standard -> *args -> Keyword-only -> **kwargs.
Default Value Pitfalls
: default values are evaluated only once at
definition time, causing all calls to share the same object if it is mutable.
In Python, functions are objects. The default args are stored in the function's __defaults__
and __kwdefaults__ attributes when the code is first loaded.
If you use mutables as defaults and modify these objects, they stay modified throughout the program.
Unless you know exactly what you are doing (like caching), use None instead of []
or {} as default parameters.
Enclosing and Closure
Enclosing (Environment)
: refers to the scope of
the outer function in a nested structure; it is a lookup rule for variable resolution.
Closure (Entity)
: an object created when an inner
function references variables from its Enclosing Scope and is returned as a
functional unit.
What happens in the Enclosing Scope?
When power_factory(2) executes, it creates an environment where the variable exp is assigned the
value 2.
How is a Closure formed?
Even after power_factory finishes, the returned square function "carries" that environment
(exp=2) with it. This "function-plus-environment" entity is the closure.
What's the necessity and why they are linked?
Without Enclosing Scope, the Closure has no
place to store data.
Without the Closure, the Enclosing Variables
would be destroyed the moment the function finishes.
The Closure is a way to "carry or memorize" the
the Environment with it.
Key Takeaway
A Closure can leverage the Enclosing Scope
to "persist" data without relying on global variables.
Scope and LEGB
LEGB
: the lookup order Python follows for Name Resolution; when
searching variable names, Python starts from your immediate vicinity and expands outward by order.
L (Local)
:
variables defined within a function or lambda; created at call time and destroyed once the function returns.
E (Enclousing)
:
scope of the outer function in a nested structure; accessible to the inner function but it doesn't own.
G (Global)
:
variables defined at the top level of a module (.py file); visible throughout the entire file.
B (Built-in)
:
names pre-defined by the Python interpreter, such as len, int, and print.
Scope
Defined In
Lifetime
Local (L)
Current function or lambda
Created at call; destroyed on return
Enclosing (E)
Outer / Parent function
Persists as long as the Closure instance exists
Global (G)
Top level of the script or module
Until the script ends
Built-in (B)
Python Interpreter internals
Always available while Python is running
global, nonlocal
:
in the L scope, you can read variables from G
and E scopes directly; however, to modify them, you must explicitly specify
global or nonlocal; otherwise, Python will treat the assignment as the creation
of a new L variable.
Functional
lambda
:
a simple, one-time anonymous function defined with a single expression lambda arguments: expression.
lambda pitfall
:
enclosing scope variable reference.
Higher-Order Functions
:
functions that take other functions as inputs or return them.
Decorator
:
higher-order functions that take a function/object as an argument and return a new function/object to replace it;
and add extra functionality (like putting on a "outer layer) without modifying the original function's source
code.
Decorator w/ Arguments
:
by default, the @decorator syntax passes only the decorated function (func) to the
decorator. If you write @timer_decorator("Log"), Python first executes
timer_decorator("Log") and expects it to return the actual decorator.
Dotted Decorator
:
essentially decorators as class methods, allowing for more flexible and stateful behavior; commonly seen in
frameworks such as Flask.
Method
Code
Feature
Decorator
@my_decorator def my_func(): pass
simple, elegant, readable
Assignment
my_func = my_decorator(my_func)
shows the decorator's underlying logic, function reassignment
Decorator for Classes
:
in principle, decorators are higher-order functions that map one object to another. Since
functions and classes
are both first-class citizens in Python, they are equally eligible for decoration; e.g., @dataclass
is designed to decorate classes.
:
methods to examine element properties at runtime such as locals(), globals(),
hasattr(), and dir(), isinstance(), issubclass(), etc.
Recursion
:
a programming technique where a function calls itself to solve a smaller instance of the same problem until it
reaches a base case; it's elegant, but memory intensive and difficult to debug.
Note: Python has a default recursion step limit, you can check and modify it as above.