Python function attributes - uses and abuses
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
Python functions are objects, and like any object they can have arbitrary attributes attached to them. Function attributes are set with func.attr = value and accessed with func.attr. Legitimate uses include memoization caches, call counters, metadata for decorators, and plugin registration systems. Abuses include using function attributes as a substitute for classes, storing unrelated global state, or hiding critical application logic in attributes that are invisible to code reviewers.
Setting and Reading Function Attributes
Function attributes persist across calls because the function object itself is a long-lived object in the module's namespace. This makes them useful for simple counters and caches without external state.
Built-in Function Attributes
Every function has __name__, __doc__, __module__, __defaults__, __annotations__, and __dict__. Custom attributes go into __dict__.
Use Case: Memoization Cache
This pattern predates functools.lru_cache and is still useful when you need direct access to the cache (for invalidation, inspection, or serialization).
Use Case: Decorator Metadata
Web frameworks like Flask use this pattern internally to associate URL routes with handler functions.
Use Case: Plugin Registration
Abuse: Using Attributes as Global State
When a function accumulates multiple related attributes, it should be refactored into a class. Function attributes lack initialization, encapsulation, and instance separation.
Abuse: Hidden Dependencies
Function attributes are invisible in the function signature. Readers of the function have no way to know that authenticate.secret_key must be set before calling the function.
Common Pitfalls
- No initialization guarantee: Unlike class
__init__, there's no mechanism to ensure function attributes are set before the function is called. Accessing an unset attribute raisesAttributeError. - Not thread-safe: Function attributes are shared mutable state across all threads. Concurrent access to
func.counter += 1causes race conditions. Usethreading.Lockor avoid mutable function attributes in multi-threaded code. - Decorators can lose custom attributes: Wrapping a function with
@functools.wrapspreserves__name__and__doc__but not custom attributes. Usefunctools.update_wrapperwith theupdatedparameter to copy__dict__. - Testing is harder: Function attributes persist between test cases because the function object is module-level. You must manually reset attributes in test setup/teardown, or your tests will interfere with each other.
- Readability suffers at scale: A function with many attributes is a class in disguise. If you find yourself adding more than one or two attributes, refactor to a class with proper encapsulation.
Summary
- Functions are objects — custom attributes are stored in
func.__dict__ - Good uses: call counters, memoization caches, decorator metadata, plugin registration
- Bad uses: substitute for classes, hidden global state, critical configuration
- Built-in attributes (
__name__,__doc__,__module__) are always available - Function attributes are not thread-safe — use locks for concurrent access
- If a function has more than one or two custom attributes, refactor to a class
Related reading
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.