Include the __slots__
and __init__
as keys in the dict
passed to type
:
def formulate(name, **data):
def __init__(self): # 1
for key, val in data.items():
setattr(self, key, val) # 2
struct = type(name, (), {'__slots__': tuple(data.keys()), '__init__': __init__})
return struct
Bar = formulate('Bar', **dict(a=1, b=2)) # 3
bar = Bar() # 4
print('__dict__' in dir(bar))
# False
print('__slots__' in dir(bar))
# True
print(bar.__slots__)
# ['a', 'b']
print(bar.a)
# 1
Note that I change the definition of __init__
from
def __init__(self, **data):
to
def __init__(self):
If you use the original version, then the data
used to update the
slot keys must be provided by the call to Bar
:
bar = Bar(**dict(a=1, b=2))
That would be bad since it would require you to repeat the same dict
twice -- once at #4 and also at #3.
It seems more likely that you want #3 to control the initial values
of the slot keys. In that case, do not list data
as an argument to
__init__
. By omitting it, Python uses the LEGB rule to look
up the value of data
. It would find it in the enclosed scope of
the formulate
function. __init__
is then said to be a closure.
Since a class with __slots__
does not have a __dict__
attribute
by default, we can't/shouldn't use self.__dict__.update(data)
.
Instead, set the slot keys with setattr
.