Skip to main content

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

Project description

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

functor-0.0.1b1.dev0.tar.gz (9.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

functor-0.0.1b1.dev0-py3-none-any.whl (7.7 kB view details)

Uploaded Python 3

File details

Details for the file functor-0.0.1b1.dev0.tar.gz.

File metadata

  • Download URL: functor-0.0.1b1.dev0.tar.gz
  • Upload date:
  • Size: 9.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.10.18

File hashes

Hashes for functor-0.0.1b1.dev0.tar.gz
Algorithm Hash digest
SHA256 bc3d7a029b408dc337b76a67514c03bfd354f96807e69481a2c0fef26eed7dd1
MD5 d3c0eafb3be323ec8658356a26d23667
BLAKE2b-256 96768472371631deb19ea6f657b00f0e9684b6bde44d2a767b0c99a8311676da

See more details on using hashes here.

File details

Details for the file functor-0.0.1b1.dev0-py3-none-any.whl.

File metadata

  • Download URL: functor-0.0.1b1.dev0-py3-none-any.whl
  • Upload date:
  • Size: 7.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.10.18

File hashes

Hashes for functor-0.0.1b1.dev0-py3-none-any.whl
Algorithm Hash digest
SHA256 bdbc54678a174e22e5a0d803b52191c0afa308a056b10af8d898715a7f471f70
MD5 d7a923b61b46f4ae205178f1466a3965
BLAKE2b-256 43baf348377b0250f7440f0a23cc501416ff6d02f8734f831c5a3f60f14e47b4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page