While searching for a convenient method to initialize slots, I had the stupid? idea of wrongly? using __slots__
dictionaries as shown below.
Note: There is a related question on SO where I've previously posted my idea as answer, but I though it might be more useful to create a new question from it as I'd really like to get more feedback.
So I'd appreciate any notes/advice/issues for the following "trick":
class Slotted:
__slots__ = {}
def __new__(cls, *args, **kwargs):
inst = super().__new__(cls)
for key, value in inst.__slots__.items():
setattr(inst, key, value)
return inst
class Magic(Slotted):
__slots__ = {
"foo": True,
"bar": 17
}
magic = Magic()
print(f"magic.foo = {magic.foo}")
print(f"magic.bar = {magic.bar}")
magic.foo = True
magic.bar = 17
Is it ok/safe to do this? Are there any drawbacks or possible probelms, etc.?
Edit:
After Alex Waygood mentioned the documentation purpose in Python 3.8+, I came up with an extension that also includes a correction for subclassing further - now it's gotten a bit lengthy though:
class Slot(str):
__slots__ = ["init"]
def __new__(cls, init, doc=""):
obj = str.__new__(cls, doc)
obj.init = init
return obj
def __call__(self):
return self.init
class Slotted:
__slots__ = {}
def __new__(cls, *args, **kwargs):
obj = super().__new__(cls)
for base in reversed(cls.__mro__[:-1]):
if isinstance(base.__slots__, dict):
for key, value in base.__slots__.items():
if isinstance(value, Slot):
setattr(obj, key, value())
else:
raise TypeError(
f'Value for slot "{key}" must'
f' be of type "{Slot.__name__}"'
)
return obj
class Magic(Slotted):
"""This class is not so boring any more"""
__slots__ = {
"foo": Slot(2, doc="Some quite interesting integer"),
"bar": Slot(3.1416, doc="Some very exciting float")
}
help(Magic)
magic = Magic()
print(f"magic.__slots__ = {magic.__slots__}")
print(f"magic.foo = {magic.foo}")
print(f"magic.bar = {magic.bar}")
Help on class Magic in module __main__:
class Magic(Slotted)
| Magic(*args, **kwargs)
|
| This class is not so boring any more
|
| Method resolution order:
| Magic
| Slotted
| builtins.object
|
| Data descriptors defined here:
|
| bar
| Some very exciting float
|
| foo
| Some quite interesting integer
|
| ----------------------------------------------------------------------
| Static methods inherited from Slotted:
|
| __new__(cls, *args, **kwargs)
| Create and return a new object. See help(type) for accurate signature.
magic.__slots__ = {'foo': 'Some quite interesting integer', 'bar': 'Some very exciting float'}
magic.foo = 2
magic.bar = 3.1416