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.
- 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.
pip install functorStandard 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: 2Recursion 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: 120functor 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: 120functor 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: 120It 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'>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.
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.
- 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