class TaskInput:
def __init__(self):
self.cfg = my_config #### Question: How do I do this only once?
class TaskA(TaskInput):
def __init__(self):
pass
class TaskB (TaskInput):
def __init__(self):
pass
- There are many tasks like
TaskA
,TaskB
etc, they all are inherited fromTaskInput
. Tasks
also depend on something, let's say, aconfiguration
which I only want to set ONCE.- The code has multiple
Tasks
classes, likeTaskA
,TaskB
etc. They all depend on this commonconfiguration
.
One natural way would be to make this configuration a class member
of TaskInput
, ie, TaskInput.cfg = my_config
, something that's initialized in __init__()
of TaskInput
.
However, if it's a member of TaskInput
, it'll get executed
multiple times, every time a new object
of type TaskX
is created as all those Tasks
are inherited from TaskInput
.
What's the best practice and best way to accomplish this in Python?
CodePudding user response:
Make the configuration a class attribute by defining it on the class rather than in __init__
.
class TaskInput:
cfg = my_config
It is now accessible as self.cfg
on any instance of TaskInput
or its children.
CodePudding user response:
I will not try to guess your need so I will assume you mean exactly what you said below, namely that you want a single initialization of a class member, but done through the creation of an instance.
a class member of TaskInput, ie, TaskInput.cfg = my_config, something that's initialized in init() of TaskInput.
This can work, but not the way you did it. in your code you never created a class attribute, anything created with self is an instance attribute belonging to a single specific task instance so that:
from copy import deepcopy
class TaskInput:
_cfg = None # prefix with '_' to indicate it should be considered private
def __init__(self, my_config=None):
_cfg = _cfg or my_config
@property
def cfg(self):
""" If you want to privatize it a bit more,
make yourself a getter that returns a deep copy."""
return deepcopy(cfg)
Now, there basically is no such thing as true privatization in python and you will never be able to entirely prevent manipulation. In the example above, any child has direct read-write access to _cfg
, so it would fall on us not to use it directly and pass by its accessors (__init__()
and cfg()
).
There's always a way to make things more difficult, like the following, using modules.
Project
├─ __init__.py
├─ settings.py
├─ module1.py
└─ module2.py
settings.py
cfg = None
module1.py
from copy import deepcopy
import settings
class A:
def __init__(self, cfg_=None):
settings.cfg = settings.cfg or cfg_
@property
def cfg(self):
return deepcopy(settings.cfg)
module2.py
""" The following classes won't be able to
overwrite the config without importing
from settings.py.
"""
from module1 import A
class B(A):
pass
class C(A):
def __init__(self):
super().__init__("foobar")
Giving these results:
b0 = B()
b0.cfg
# > None
b1 = B({"foo1": "bar1"})
b1.cfg
# > {'foo1': 'bar1'}
b2 = B({"foo1": "bar2", "foo3": "bar3"})
b2.cfg
# > {'foo1': 'bar1'}
try:
b2.cfg = 1234
except Exception as e:
print(type(e), e)
# > <class 'AttributeError'> can't set attribute
b2.cfg
# > {'foo1': 'bar1'}
c = C("asdf")
c.cfg
# > {'foo1': 'bar1'}
Which can be overkill of course and removes the actual ownership of the configuration from the class