Build Functions in Python - With Funtools, Decorators and Lambda


Script demonstrates decorators and lambda functions by implementing a simple task:
timing function execution and
processing a list of numbers with lambda-based operations (e.g., square, cube).
Objective: Understand Decorators and Lambda
What New Concept
Decorators:
What: Functions that modify other functions (e.g.,
timing_decorator
adds timing toprocess_numbers
).Why: Adds functionality (e.g., logging, timing) without changing the original function.
Where: Used in frameworks (e.g., Flask for routing) or DevOps scripts (e.g., logging automation tasks).
New: Uses
@decorator
syntax andwraps
to preserve function metadata.Exceptions: Incorrect decorator implementation can alter function behavior (e.g., missing
@wraps
loses metadata).
Lambda Functions:
What: Anonymous functions defined inline (e.g.,
lambda x: x ** 2
).Why: Concise for simple operations passed as arguments.
Where: Common in functional programming (e.g.,
map()
,filter()
) or callbacks.New: Defined with lambda args: expression; here, used for squaring/cubing numbers.
Exceptions: Limited to single expressions; complex logic needs regular functions.
Callable Type:
What: Type hint
Callable[[float]
, float] for functions taking a float and returning a float.Why: Specifies function signatures in type hints for clarity.
Where: Used in professional code with dynamic function passing.
New: Requires
typing.Callable
(one of fewtyping
imports needed in Python 3.12).Exceptions: Incorrect type usage caught by
mypy
.
Function Metadata (@wraps
):
What: Preserves original function’s name and docstring in decorators.
Why: Ensures decorated functions remain introspectable (e.g., for debugging).
Where: Standard in decorator implementation.
New: Imported from
functools
.Exceptions: Omitting
@wraps
can cause confusion in debugging.
Project summary
Process a list of numbers and return valid input with operations and time consumed, if provide empty raise an
ValueError
and if list contain other data type than numbers then they consider invalid input.Uses
@decorator
syntax andwraps
to preserve function metadata.Imported from
functools
.Defined with lambda args: expression; here, used for squaring/cubing numbers.
Requires
typing.Callable
(one of fewtyping
imports needed in Python 3.12).
Structure
core-python
├── basics
│ └── functions
│ ├── README.md
│ └── main.py
└── tests
└── test_functions.py
main.py
functions script.test_functions.py
test script for email validator.
How I Built it
Build a pseudo flow with pen and paper.
Start writing code and also my experience on how I end up with issues:
Let’s start with
main()
:Provide input to
process_numbers()
.numbers
assign to a list of float values.
Let’s understand
lambda arg: expression
square
assign to lambda function to do the operationx**2
on everyx
argument.cube
assign to lambda function to do the operationx**3
on everyx
argument.
- Here the
numbers
list andsquare
/cube
lambda operations use as a parameter ofprocess_numbers()
, whose output store insquared_numbers
/cube_numbers
which later get print.
Here the empty list and
square
lambda operation use as a parameter ofprocess_numbers()
, whose output store inempty_result
which later get print.I need to do with
try … except
because when if empty list provide toprocess_number()
it raiseValueError
but after that program just stop, so to counter that I make one heremain()
. Here the value ofValueError
inprocess_numbers()
passed to this except and printe
.
Here I provide invalid input, which means list contain other data type than numbers.
However, when invalid send to
process_numbers()
the invalid one like”abc”
taken out and only valid input process with operation i.esquare
.
This are the built-in modules that needed:
time
use fortyping
use for type test, here it use to importCallable
use if variable assign to a function (e.glambda
)Any
use for considering variable assign to any data type.
functools
is a tools for working with functions and callable objects.wraps
Preserves original function’s name and docstring in decorators.
To know more about modules, libraries and keyword:
Open Terminal
pydoc3.x <library_name/module_name/keyword> # eg. pydoc3.10 time # eg. pydoc3.10 print
Next
process_numbers()
timing_decorator
adds timing toprocess_numbers
Argument:
numbers
take input list.operation
take lambda variable (e.g. square/cube)
Return format:
→ list[float]
means list of float data type.Return: list of valid input which went to operation.
This above is the refactor and recently updated one. I need to fixed it few times because I unnecessary put type conversion at the time calling operation()
so I get an TypeError
error which later can be handle with error handling. Anyway here is the previous way I to handle error:
try:
if num + "":
# >>> 2 + ""
# TypeError: unsupported operand type(s) for +: 'int' and 'str'
invalid_results.append(num)
except TypeError: # Here TypeError be handle
results.append(operation(num))
## Other way
result = results.append(operation(num))
invalid = invalid_results.append(num)
# using `type() .. is not`
if type(num) is not str:
result
else:
invalid
# using `type() .. in`
result if type(num) in (int, float) else invalid
# using isinstance()
if isinstance(num, str):
invalid_results.append(num)
else:
results.append(operation(num))
- Finally
timing_decorator
adds timing toprocess_numbers
.
How to run
Change directory to
core-python
, so that test can also be performed.# optional: in python-foundation dir # source venv/bin/activate # Windows: venv\Scripts\activate cd core-python
Run
main.py
.python3 basics/functions/main.py
Output:
Processing numbers with square operation: process_numbers took 0.0000 seconds Squared: [1.0, 4.0, 9.0, 16.0, 25.0] Processing numbers with cube operation: process_numbers took 0.0000 seconds Cubed: [1.0, 8.0, 27.0, 64.0, 125.0] Processing empty list: Error - Input list cannot be empty Processing invalid input: Skipped invalid inputs: ['abc'] process_numbers took 0.0000 seconds Invalid input result: [1.0, 9.0]
Run
test_functions.py
:PYTHONPATH=. pytest tests/test_functions.py -v
Output:
$ PYTHONPATH=. pytest tests/test_functions.py -vv ========================== test session starts =========================== platform linux -- Python 3.12.8, pytest-8.4.0, pluggy-1.6.0 -- /PATH/TO/PYTHON-FOUNDATION/.venv/bin/python3.12 cachedir: .pytest_cache rootdir: /PATH/TO/PYTHON-FOUNDATION/core-python plugins: anyio-4.9.0 collected 4 items tests/test_functions.py::test_process_numbers_valid_input PASSED [ 25%] tests/test_functions.py::test_process_numbers_invalid_input PASSED [ 50%] tests/test_functions.py::test_process_numbers_empty_list PASSED [ 75%] tests/test_functions.py::test_process_numbers_invalid_operation PASSED [100%] =========================== 4 passed in 0.01s ============================
Exceptions/Edge Cases
Decorator Issues: Missing
@wraps
could obscure function metadata (handled withfunctools.wraps
).Invalid Inputs: Empty lists or non-numeric inputs (handled with
ValueError
,TypeError
).Type Hints: Use
mypy
for static checking:pip install mypy mypy core-python/basics/functions.py
Performance: Timing decorator may show negligible times for fast functions.
Next
Control Structure explore control structures with Python 3.12’s structural pattern matching (match statement) and introduce sets for unique data handling.
Links
Github: Repo - Python Foundation/functions
🤝 How to Contribute / Follow Along
Clone the repo or copy scripts to try on your own machine.
Share your output or improvements in the comments.
DM me on GitHub or Hashnode for feedback!
📢 Let’s Connect
Follow me on Hashnode to get notified when I publish new Python projects 👇
➡️ Chetan Tekam
Subscribe to my newsletter
Read articles from Chetan Tekam directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
