Declare a proxy property to a child object
Tags: python
Published: 2022-05-15 15:13:10

We want to expose an attribute on a facade object, while it is implemented by a child object. Python allows us to do this by declaring a read/write property.

```python
class Foo:

	def __init__(self):
		self.var1 = "hello"

class Bar:

	def __init__(self):
		self.foo = Foo()

	@property
	def var1(self):
		return self.foo.var1

	@var1.setter
	def var1(self, value):
		self.foo.var1 = value
```

In this code, the `var1` property in `Bar` is a proxy to the `var1` attribute of the `foo` instance.


With this implementation, each time we add an attribute in `Foo` and want to expose it in `Bar`, we have to declare 6 lines of codes. This boilerplate will grow and make the class more difficult to read and maintain.

We will implement a `descriptor` `proxy_property` to reduce this boilerplate to a single line. Here is the reimplemented `Bar` class.

```python
class Bar:
	var1 = property_proxy("foo", "var1")

	def __init__(self):
		self.foo = Foo()
```

A `descriptor` in python is a class in which we implement the dunder methods `__get__`, `__set__` and `__delete__`.


> Descriptors are a powerful, general purpose protocol. They are the mechanism behind properties, methods, static methods, class methods, and super(). 
> 
> [Python Doc - Descriptor HowTo Guide](https://docs.python.org/3/howto/descriptor.html#technical-tutorial)

Here is the implementation of the `property_proxy` descriptor.


```python
class property_proxy(object):
    """
    A descriptor based recipe that makes it possible to write shorthands
    that forward attribute access from one object onto another.

    >>> class B:
    >>>     def __init__(self):
    >>>         self.foo = 12
    >>>         self.bar = 12
    >>>
    >>> class A:
    >>>     foo: int = property_proxy("b", "foo")
    >>>     bar: int = property_proxy("b", "bar")
    >>>
    >>>     def __init__(self):
    >>>         self.b = B()
    >>>
    >>> a = A()
    >>> print(a.foo)

    This descriptor avoids writing the code below to establish a proxy
     with a child instance

    >>> class B:
    >>>     def __init__(self):
    >>>         self.foo = 12
    >>>
    >>> class A:
    >>>
    >>>     def __init__(self):
    >>>         self.b = B()
    >>>
    >>>     @property
    >>>     def foo(self):
    >>>         return self.b.foo
    >>>
    >>>     @foo.setter
    >>>     def foo(self, value):
    >>>         self.b.foo = value
    >>>

    """

    def __init__(self, objectName, attrName):
        self.objectName = objectName
        self.attrName = attrName

    def __get__(self, instance, owner=None):
        proxy = getattr(instance, self.objectName)
        if proxy is None:
            raise ValueError(f"{self.objectName} does not exists on {instance}")

        return getattr(proxy, self.attrName)

    def __set__(self, instance, value):
        proxy = getattr(instance, self.objectName)
        if proxy is None:
            raise ValueError(f"{self.objectName} does not exists on {instance}")

        setattr(proxy, self.attrName, value)
```

### To conclude

In this post, we found that exposing a child object's attribute in a facade would take a large boilerplate from us and interfere with reading and maintaining our facade.

We implemented a `property_proxy` descriptor to make this declaration in one line and improve code consistency. The gain in readability is significant.

```python
class Bar:
	var1 = property_proxy("foo", "var1")

	def __init__(self):
		self.foo = Foo()
```

### Limit

The `property_proxy` declaration has 2 flaws which affect the maintainability of the code. This declaration does not allow a linter like `mypy` to check the existence and typing of attributes. The IDE's refactoring of an attribute name may miss this declaration because it will consider the attributes in the `property_proxy` declaration as plain text.

## Read more
* [Python Doc - Descriptor HowTo Guide](https://docs.python.org/3/howto/descriptor.html#technical-tutorial)