This release is a pre-release and may not be stable for production use.
functor
The package functor is a Python black magic library that bridges the gap between functions and objects. It upgrades standard functions into Function Objects (Functors), allowing them to access their own instance via functor.self from within their own scope.
This enables state persistence, self-reference (recursion), and metadata management without needing to pass an explicit self argument or write boilerplate class structures.
✨ Key Features
- Implicit Context Injection: Access the current function instance directly via
functor.selfinside the decorated function. - State Persistence: Functions are objects. You can attach attributes to them just like class instances.
- Module as a Class: The library leverages the "Module as a Class" pattern. The
functormodule itself is callable and acts as the factory.
📦 Installation
pip install functor
🚀 Quick Start
1. Stateful Functions
Standard Python functions cannot easily retain state without using global or nonlocal. With @functor, functions behave like single-method classes:
import functor
@functor
def counter():
# Initialize state on the function instance itself
if not hasattr(functor.self, 'count'):
functor.self.count = 0
functor.self.count += 1
return functor.self.count
print(counter()) # Output: 1
print(counter()) # Output: 2
print(counter.count) # Output: 2
2. Recursion & Self-Reference
Recursion usually requires hardcoding the function name. functor allows you to decouple the logic from the name:
import functor
@functor
def factorial(n):
if n == 0:
return 1
# Use functor.self to call the function recursively
# This works even if the function is assigned to a different variable name
return n * functor.self(n - 1)
print(factorial(5)) # Output: 120
3. Bound Mode
functor can also be used inside classes. By using bound=True, you can control how the instance binding works.
import functor
@functor(bound=True)
def factorial(self, n):
if n == 0:
return 1
# Use self to call the function recursively
return n * self(n - 1)
print(factorial(5)) # Output: 120
4. Inheritance of functor
functor can also be used inside classes. By using bound=True, you can control how the instance binding works.
from functor import functor
class Factorial(functor):
def minus(self, n, value):
return self(n - value)
@Factorial
def factorial(n):
if n == 0:
return 1
return n * Factorial.self.minus(n, 1)
print(factorial(5)) # Output: 120
Module as a Class
It can be noticed that import functor can be used simply for @functor. This is because the library replaces the standard module object in sys.modules with a custom FunctorModule instance.
import functor
# 'functor' is a module which is callable
print(type(functor)) # <class 'functor.FunctorModule'>
Nonetheless, from functor import functor can also be used.
from functor import functor
# 'functor' is a type
print(type(functor)) # <class 'type'>
📝 API Reference
@functor / @functor(bound=True)
The core decorator/constructor.
- function (
FunctionType): The function to wrap. It should contain a valid__code__attribute in principle. - bound (
bool): Whether to bind the first argument to the current functor instance. Defaults toFalse.
Returns: A callable functor instance that proxies the original function while enabling state management.
functor.self
A magic accessor valid only inside a function decorated with @functor when not bound. It returns the current functor instance.
- Raises:
RuntimeErrorif accessed outside of a valid context.
⚠️ Caveats
- Performance: Because the library relies on
sys._getframeand global variable proxying, there is a slight overhead compared to standard function calls. It is best used for logic-heavy or stateful operations, not tight loops in critical paths. - Debugging: Since the function's
__globals__are swapped for a proxy during execution, some debuggers might display the environment differently than expected.
License: MIT
Metadata
Release files for functor 0.0.1b1.dev0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| functor-0.0.1b1.dev0.tar.gz | 9.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| functor-0.0.1b1.dev0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 16.9 kB
Release files / functor-0.0.1b1.dev0.tar.gz
| Download URL | functor-0.0.1b1.dev0.tar.gz |
|---|---|
| Size | 9.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bc3d7a029b408dc337b76a67514c03bfd354f96807e69481a2c0fef26eed7dd1
|
|
BLAKE2b-256 checksum How to use checksums |
96768472371631deb19ea6f657b00f0e9684b6bde44d2a767b0c99a8311676da
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.10.18
|
Release files / functor-0.0.1b1.dev0-py3-none-any.whl
| Download URL | functor-0.0.1b1.dev0-py3-none-any.whl |
|---|---|
| Size | 7.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bdbc54678a174e22e5a0d803b52191c0afa308a056b10af8d898715a7f471f70
|
|
BLAKE2b-256 checksum How to use checksums |
43baf348377b0250f7440f0a23cc501416ff6d02f8734f831c5a3f60f14e47b4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.10.18
|