0

I'm trying to build a python3 module for an HTTP RESTful API that I've coded.

My idea was to create a base class that should have a request.Session() attribute so I can assign an authorization token header to that and don't worry about it anymore and also a logger function and so on. The problem is that a class called User inherits from 2 classes: PublicUser and base and I can't initialize them correctly.

It's the first time that I'm working with inherited class so obviously I'm missing something.

This is my folder structure:

examplemodule/
  |--> __init__.py
  |--> classes/
        |-->  base.py
        |-->  user.py

base.py

from requests import Session
from requests.sessions import session


class Logger:
    def __init__(self):
        pass

    def log(self, message):
        print(message)


class Base:
    def __init__(self, token=None):
        if not hasattr(self, 'logger'):
            self.logger = Logger()
        if not hasattr(self, 'session'):
            self.session = Session()
            self.session.headers.update(
                {'authorization': 'Token {}'.format(token)}
            )
            # Try to login to see if token is valid, if not raise exception
            # If token is valid then the retrieved user json is saved
            self._user = {
                'id': 1,
                'username': 'test1',
                'email': '[email protected]'
            }

user.py

from .base import Base

PUBBLIC_USER_ATTRS = ['id', 'username']
PRIVATE_USER_ATTRS = ['email']


class PublicUser:
    def __init__(self, user):
        for k in PUBBLIC_USER_ATTRS:
            setattr(self, k, user[k])


class User(Base, PublicUser):
    def __init__(self, token=None):
        super(Base, self).__init__(token=token)
        super(PublicUser, self).__init__(self._user)
        for k in PRIVATE_USER_ATTRS:
            setattr(self, k, self._user[k])

__init__.py

from .classes.user import User

then to test my module I run:

import examplemodule
examplemodule.User(token='')

but unfortunately I get a TypeError at super(Base, self).__init__(token=token)

TypeError: super() takes no keyword arguments

What is the best way to get through this?

2 Answers 2

3

super is meant for cooperative inheritance, where all involved classes are using super in a way that ensures all necessary methods are called. That means super should also be used by base classes, even if all they inherit from is object.

class Base:
    def __init__(self, *, token=None, **kwargs):
        super().__init__(**kwargs)
        if not hasattr(self, 'logger'):
            self.logger = Logger()
        if not hasattr(self, 'session'):
            self.session = Session()
            self.session.headers.update(
                {'authorization': 'Token {}'.format(token)}
            )
            # Try to login to see if token is valid, if not raise exception
            # If token is valid then the retrieved user json is saved
            self._user = {
                'id': 1,
                'username': 'test1',
                'email': '[email protected]'
            }

class PublicUser:
    def __init__(self, *, id, username, **kwargs):
        super().__init__(**kwargs)
        self.id = id
        self.username = username
        
class User(Base, PublicUser):
    def __init__(self, *, email, **kwargs):
        super().__init__(**kwargs)
        self.email = email


u = User(token='...', id='...', username='...', email='...')

User.__init__ only has to make one call to super().__init__, knowing that its base classes also use super().__init__ to always call the next __init__, until reaching the end of the MRO. You start with User.__init__, which calls Base.__init__, which calls PublicUser.__init__ (not object.__init__), which finally calls object.__init__.

At each step, the remaining keyword arguments are split between the "known" arguments, which are handled, and the "unknown" arguments, which are passed up the line. Eventually, all keyword objects should be extracted and handled by the time object.__init__ is called.

See https://rhettinger.wordpress.com/2011/05/26/super-considered-super/ for a more thorough explanation of how this works in practiced (in particular, why keyword arguments are preferred over positional arguments).

Sign up to request clarification or add additional context in comments.

Comments

1

You probably want to call constructors of super-classes like this:

Base.__init__(self, token=token)
PublicUser.__init__(self, self._user)

2 Comments

What are the differences between this approach and @chepner 's one?
it is just my opinion but I consider this more explicit and readable. That is only difference I see. You can use normal arguments which is more self documenting then just **kwargs

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.