Search code examples
python-3.xinheritanceclass-methodpython-class

How to properly inherit class method


I have a database connection class that creates a connection pool. Now as the application grows and I'm adding different types of database writers, I want to move database connections to a separate class and inherit from it. So far I have this:

class ServiceDB:
    @classmethod
    async def init(cls, settings):
        self = ServiceDB()
        self.pool = await asyncpg.create_pool(
            database=settings["POSTGRES_DB"],
            user=settings["POSTGRES_USER"],
            password=settings["POSTGRES_PASSWORD"],
            host=settings["DB_HOST"],
            port=settings["DB_PORT"],
        )
        return self



class ChildWriter(ServiceDB):
    async def write_db(self, query):
        # Write to specific table
        pass



if __name__ == "__main__":
    settings = {'info': "some connection settings"}
    query = "SELECT * FROM 'table'"
    connection = await ChildWriter().init(settings)
    await connection.write_db(msg, query)

When I run this I get AttributeError: 'ServiceDB' object has no attribute 'write_db'. How do I properly extend ServiceDB with the write_db method?


Solution

  • Classmethods receive the "current class" as the first argument. Instantiate this cls, not the fixed baseclass.

    class ServiceDB:
        @classmethod
        async def init(cls, settings):
            self = cls()  # cls is the *current* class, not just ServiceDB
            self.pool = await asyncpg.create_pool(
                database=settings["POSTGRES_DB"],
                user=settings["POSTGRES_USER"],
                password=settings["POSTGRES_PASSWORD"],
                host=settings["DB_HOST"],
                port=settings["DB_PORT"],
            )
            return self
    

    Note that ideally, all attributes are set via __init__ instead of a separate classmethod constructor. The separate constructor should just pass on any attributes constructed externally.

    class ServiceDB:
        def __init__(self, pool):
            self.pool = pool
    
        @classmethod
        async def init(cls, settings, **kwargs):
            pool = await asyncpg.create_pool(
                database=settings["POSTGRES_DB"],
                user=settings["POSTGRES_USER"],
                password=settings["POSTGRES_PASSWORD"],
                host=settings["DB_HOST"],
                port=settings["DB_PORT"],
            )
            return cls(pool=pool, **kwargs)
    
    class ChildWriter(ServiceDB):
        async def write_db(self, query): ...
    
    if __name__ == "__main__":
        settings = {'info': "some connection settings"}
        query = "SELECT * FROM 'table'"
        # call classmethod on class   V
        connection = await ChildWriter.init(settings)
        await connection.write_db(msg, query)