Python: How to access parent class instance methods without inheritance?

Viewed 315

Base class with abstract method _conn()

from cached_property import cached_property

class Base:
    
    def __init__(self, conn_id):
        """Store secrets details"""
        self.conn_id = conn_id
    
    @cached_property    
    def _conn(self):
        """Fetch secrets and generate authenticated client"""
        raise NotImplemented
    
    def get_conn(self):
        """Return authenticated client"""
        return self._conn

Parent classes

from cached_property import cached_property

class Parent(Base):

    @cached_property
    def _conn(self):
        """Generate authenticated client specific for Parent"""
        client = ...
        return client

Child classes

from typing import List, Optional
from pydantic.dataclasses import dataclass


@dataclass
class Toy:
    name: str
    type: str
    
    def get_color(self) -> str:
        color = self.get_conn().get_toy_color(...)
        return color
    

@dataclass
class Child:
    first_name: str
    last_name: str
    ...

    def list_all_toys(self) -> List[Toy]:
        all_toys = self.get_conn().fetch_all_toys(...)
        return [Toy(name=x.toy_name, type=x.toy_type) for x in all_toys]

    def get_favorite_toy(self) -> Optional[Toy]:
        favorite_toy = self.get_conn().fetch_favorite_toy(...)
        if not favorite_toy:
            return None
        return Toy(name=favorite_toy.toy_name, type=favorite_toy.toy_type)

(Ideal) Usage

parent = Parent(conn_id='my-secret-connection-details')

child_1 = parent.Child(first_name='John', last_name='Doe')

for each_toy in child_1.list_all_toys():
    print(f"{child_1.first_name}'s toy {each_toy.name} is a {each_toy.get_color()} {each_toy.type}.")
    # John's toy Teddy is a white stuffed bear.

Important notes

  • The parent class can be an ordinary Python class, but the children classes should be Python/Pydantic dataclasses.
  • The goal of using the same get_conn() method is to reduce the number attempts to fetch credentials and to authenticate.

I've thought about solving this using a @classmethod of the children classes that returns an authenticated instance. Seemed promising until I realized that dataclasses don't allow you to modify their __init__ method. For example:

from typing import Callable, Optional
from pydantic.dataclasses import dataclass

@dataclass
class Toy:
    ...
    @classmethod
    def generate_with_connection(cls, connection: Callable, *args, **kwargs):
        return cls(*args, **kwargs, connection=connection) # Requires logic in __init__ to handle `connection`.

@dataclass
class Child:
    ...
    def get_favorite_toy(self) -> Optional[Toy]:
        favorite_toy = self.get_conn().fetch_favorite_toy(...)
        if not favorite_toy:
            return None
        return Toy.generate_with_connection(
            connection=self.get_conn,
            name=favorite_toy.toy_name,
            type=favorite_toy.toy_type
        )

Questions

  1. How do you link the parent class and multiple children classes to ensure that each child class can access the same get_conn() method of the parent class? My first guess would be inheritance but I don't think it solves the next question. Are there other ways using the inspect/traceback modules?
  2. How do we ensure that the methods of each children class can return instances of other children classes that also have access to the same get_conn() method of the parent class? For example: Child.get_favorite_toy() should return an instance of Toy which can successfully run Toy.get_color() using the same get_conn() method.
0 Answers
Related