Skip to content

Caewinix/functor

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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.self inside 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 functor module 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 to False.

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: RuntimeError if accessed outside of a valid context.

⚠️ Caveats

  1. Performance: Because the library relies on sys._getframe and 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.
  2. Debugging: Since the function's __globals__ are swapped for a proxy during execution, some debuggers might display the environment differently than expected.

License: MIT

About

The `functor` package uses Python black magic to turn functions into objects (functors), letting them access their own instance via `functor.self`.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages