Struct Embedding in Python

Python supports composition of types through a concept similar to embedding, often referred to as composition or delegation. This is different from inheritance and allows for a more flexible design.

class Base:
    def __init__(self, num):
        self.num = num

    def describe(self):
        return f"base with num={self.num}"

# A `Container` composes a `Base`. In Python, we explicitly create
# and store an instance of `Base`.
class Container:
    def __init__(self, num, string):
        self.base = Base(num)
        self.string = string

    # We delegate the `describe` method to the composed `Base` instance
    def describe(self):
        return self.base.describe()

def main():
    # When creating instances, we pass the necessary parameters
    # to initialize both the container and its composed base.
    co = Container(1, "some name")

    # We can access the base's fields through the `base` attribute
    print(f"co={{num: {co.base.num}, str: {co.string}}}")

    # We can also access the base's fields directly if we implement
    # property methods, but for this example, we'll use the explicit path
    print("also num:", co.base.num)

    # We can call methods that are delegated to the base
    print("describe:", co.describe())

    # In Python, we don't need to declare interfaces explicitly.
    # Any object with a `describe` method satisfies the "interface".
    def print_description(describer):
        print("describer:", describer.describe())

    # The `Container` instance satisfies the implicit "Describer" interface
    print_description(co)

if __name__ == "__main__":
    main()

This Python code demonstrates a concept similar to struct embedding in Go, using composition. Here’s how it works:

  1. We define a Base class with a num attribute and a describe method.

  2. We create a Container class that composes a Base instance. It also has its own string attribute.

  3. The Container class delegates the describe method to its composed Base instance.

  4. In the main function, we create a Container instance, demonstrating how to initialize both the container and its composed base.

  5. We show how to access the base’s fields through the base attribute of the container.

  6. We demonstrate calling the delegated describe method on the container.

  7. Finally, we show how Python’s duck typing allows us to use the Container instance wherever an object with a describe method is expected, similar to interface implementation in Go.

To run the program:

$ python struct_composition.py
co={num: 1, str: some name}
also num: 1
describe: base with num=1
describer: base with num=1

This example illustrates how Python can achieve similar functionality to Go’s struct embedding through composition and delegation. While the syntax and some concepts differ, the core idea of composing types and behaviors is preserved.