Skip to content
Search

Python:ProxyProperties

lastmod 2025-04-25created 2025-04-251 edits

Did you know that a class attribute can be used as both a method and an attribute?!?

This is not a built-in Python feature, but an example of what you can do by making good use of Python's magic methods and descriptors.

Code

from typing import Callable, Generic, TypeVar, ParamSpec, Self

P = ParamSpec("P")
R = TypeVar("R")
T = TypeVar("T")

class ProxyProperty(Generic[P, R]):
    func: Callable[P, R]
    instance: object

    def __init__(self, func: Callable[P, R]) -> None:
        self.func = func

    def __get__(self, instance: object, _=None) -> Self:
        self.instance = instance
        return self

    def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
        return self.func(self.instance, *args, **kwargs)

    def __repr__(self) -> str:
        return self.func(self.instance)

def proxy_property(func: Callable[P, R]) -> ProxyProperty[P, R]:
    return ProxyProperty(func)

class Container:
    @proxy_property
    def value(self, val: int = 5) -> str:
        return f"The value is: {val}"

# Example usage
c = Container()
print(c.value)      # Returns: The value is: 5
print(c.value(7))   # Returns: The value is: 7

How does this work? It works through Python's Descriptor Protocol1.

  1. The __get__ method transforms the ProxyProperty object into a descriptor.
  2. When you access c.value, Python calls __get__ which returns self (the descriptor instance).
  3. The __repr__ method handles property access (returning default values).
  4. The __call__ method handles method calls with parameters.

This creates a dual-purpose attribute that can be read directly or called like a function!

The advantage of this class is that it lets you build intuitive APIs where an attribute needs configuration or should have sensible defaults but can still be customized.

See also

Favorite site

References


  1. https://docs.python.org/3/howto/descriptor.html ↩

  2. 14_Advanced_Python_Features_-_Edward_Li's_Blog.pdf ↩

author Zer0 sitemap