Skip to main content

No project description provided

Project description

Compclasses

Like dataclasses, but for composition.

As the Gang of Four (probably) said:

favor object composition over class inheritance.

However when we use composition in Python, we cannot access methods directly from the composed class, and we either re-define these methods from scratch, or access them using chaining.

This codebase wants to address such issue and make it easy to do so, by delegating the desired methods of a class to its attributes.


Documentation: https://fbruzzesi.github.io/compclasses

Source Code: https://github.com/fbruzzesi/compclasses


Alpha Notice

This codebase is experimental and is working for my use cases. It is very probable that there are cases not covered and for which everything breaks. If you find them, please feel free to open an issue in the issue page of the repo.

Installation

compclasses is published as a Python package on pypi, and it can be installed with pip, ideally by using a virtual environment.

From a terminal it is possible to install it with:

python -m pip install compclasses

Getting Started

Let's suppose to have the following classes:

class Foo:
    """Foo class"""

    def __init__(self, value: int):
        """foo init"""
        self.value = value

    def get_foo(self):
        """get value attribute"""
        return self.value

    def hello_from_foo(self, name: str) -> str:
        """Method with argument"""
        return f"Hello {name}, this is Foo!"


class Bar:
    """Bar class"""

    def __len__(self) -> int:
        """Custom len method"""
        return 42

class Baz:
    """Baz class"""

    def __init__(self, foo: Foo, bar: Bar):
        self._foo = foo
        self._bar = bar

Now let's instantiate them and try see how we would access the inner methods/attributes:

foo = Foo(123)
bar = Bar()

baz = Baz(foo, bar)

baz._foo.get_foo()  # -> 123
baz._foo.hello_from_foo("GitHub")  # -> "Hello GitHub, this is Foo!"
baz._bar.__len__()  # -> 42

len(baz)  # -> TypeError: object of type 'Baz' has no len()

Using the compclass decorator we can forward the methods that we want to the Baz class at definition time:

from compclasses import compclass

delegates = {
    "_foo": ( "get_foo", "hello_from_foo"),
    "_bar": ("__len__", )
}

@compclass(delegates=delegates)
class Baz:
    """Baz class"""

    def __init__(self, foo: Foo, bar: Bar):
        self._foo = foo
        self._bar = bar

baz = Baz(foo, bar)
baz.get_foo()  # -> 123
baz.hello_from_foo("GitHub")  # -> "Hello GitHub, this is Foo!"
len(baz)  # -> 42

We can see how now we can access the methods directly.

Remark that in the delegates dictionary, we have that:

  • the key corresponds to the attribute name in the Baz class
  • the value should be an iterable of string corresponding to methods/attributes present in the class instance associated to the key-attribute.

delegatee class

Instead of using an iterable in the delegates dictionary, it is possible to pass a delegatee instance as a value.

Such class supports some additional features such as:

  • class attributes and methods validation.
  • it allows to pass * value to pass all non-dunder attributes/methods.
  • adding custom attributes/methods prefix and/or suffix.

Since get_foo and hello_from_foo are the only two methods of the Foo class, the previous example can be rewritten as:

from compclasses import compclass, delegatee

delegates = {
    "_foo": delegatee(Foo, attrs=["*"]),
    "_bar": delegatee(Bar, attrs=["__len__"])
}

@compclass(delegates=delegates)
class Baz:
    """Baz class"""

    def __init__(self, foo: Foo, bar: Bar):
        self._foo = foo
        self._bar = bar

baz = Baz(foo, bar)
baz.get_foo()  # -> 123
baz.hello_from_foo("GitHub")  # -> "Hello GitHub, this is Foo!"
len(baz)  # -> 42

Check the dedicated documentation page to get a better understanding and see more examples on how delegatee can be used, its pros and cons.

Why Composition (TL;DR)

Overall, composition is a more flexible and transparent way to reuse code and design classes in Python. It allows to build classes that are customized to your specific needs, and it promotes code reuse and modularity.

A more detailed explaination is present in the dedicated documentation page.

Feedbacks

Any feedback, improvement/enhancement or issue is welcome in the issue page of the repo.

Contributing

Please refer to the contributing guidelines in the documentation site.

Inspiration

This projects is inspired by the forwardable library, a "utility for easy object composition via delegation".

However I was looking for both more flexibility and more features. In particular:

  • a clear separation between class definition and method forwarding;
  • a validation step to make sure that changing something from the component doesn't break the class;
  • the possibility to forward all the methods/attributes of a given component with a single instruction;
  • the chance of adding prefix and/or suffix for each component;

Please refer to Beyond the basics page to see example usages.

Licence

The project has a MIT Licence

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

compclasses-0.1.0.tar.gz (55.4 kB view details)

Uploaded Source

Built Distribution

compclasses-0.1.0-py2.py3-none-any.whl (11.9 kB view details)

Uploaded Python 2 Python 3

File details

Details for the file compclasses-0.1.0.tar.gz.

File metadata

  • Download URL: compclasses-0.1.0.tar.gz
  • Upload date:
  • Size: 55.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.3.0 pkginfo/1.8.3 requests/2.28.1 setuptools/46.4.0.post20200518 requests-toolbelt/0.9.1 tqdm/4.64.0 CPython/3.8.3

File hashes

Hashes for compclasses-0.1.0.tar.gz
Algorithm Hash digest
SHA256 3965c2397f0f563dab938e8c18b6d7af430a2e6a32177b050a1a4c8c3f025d6d
MD5 fef747f9e37557cfbb73e5c9c9ad96f4
BLAKE2b-256 bdc05fd54c04f7fcb1d38eef92a1e2910733ca36ea96c52ee45165f52d5637a4

See more details on using hashes here.

File details

Details for the file compclasses-0.1.0-py2.py3-none-any.whl.

File metadata

  • Download URL: compclasses-0.1.0-py2.py3-none-any.whl
  • Upload date:
  • Size: 11.9 kB
  • Tags: Python 2, Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.3.0 pkginfo/1.8.3 requests/2.28.1 setuptools/46.4.0.post20200518 requests-toolbelt/0.9.1 tqdm/4.64.0 CPython/3.8.3

File hashes

Hashes for compclasses-0.1.0-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 9b32d9eb543845919a63a9db529cb80a61146324f45b8c710fa1a4c95984ae2f
MD5 f79017119f5f7a7bae33c510bcf7a645
BLAKE2b-256 826939542c60b0457c6dfec484aeda4e39721436c4a10ffdcb583bc5cbcef44e

See more details on using hashes here.

Supported by

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