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': 'test@test.com'
}
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?
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': 'test@test.com'
}
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).