SerialDev
SerialDev

Reputation: 2847

Python __slots__ metaclass issue

I'm currently implementing slotted metaclasses using type():

type(i, (), {'__slots__': tuple(data)

I would of course ideally keep with slots as I have a usecase that would benefit from their smaller memory footprint and improved access speed.

when I do:

dir(slotted_class)
>>>['__class__',
 '__delattr__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__slots__',
 '__str__',
 '__subclasshook__',
 'slotted_attribute1',
 'slotted_attribute2',
 'slotted_attribute3',
 'slotted_attribute4']

however when I run:

slottedclass.slotted_attribute1

I get the following error:

    >>> AttributeError                            Traceback (most recent call last)
<ipython-input-58-88291109fa74> in <module>()
----> 1 slotted_class.slotted_attribute1

AttributeError: slotted_attribute1

EDIT: For further information and even more confusion: If I implement an equivalent directly without the use of metaclasses:

class slottedclass_non_meta(object):
__slots__ = ['slotted_attribute1', 'slotted_attribute2', 'slotted_attribute3', 'slotted_attribute4']

def __init__(self, slotted_attribute1, slotted_attribute2, slotted_attribute3, slotted_attribute4):
    self.slotted_attribute1, self.slotted_attribute2, self.slotted_attribute3, self.slotted_attribute4 = slotted_attribute1, slotted_attribute2, slotted_attribute3, slotted_attribute4

Then make this comparison dir(slottedclass) == dir(slottedclass_non_meta)

>>> True

Upvotes: 0

Views: 484

Answers (1)

jsbueno
jsbueno

Reputation: 110311

It is probably just some error on your data variable. Make sure it is an iterable or sequence of strings.

Trying this on my interactive environment worked flawlessly:

In [162]: m = type("m", (), {"__slots__": ("test1",)})

In [163]: m.test1
Out[163]: <member 'test1' of 'm' objects>

In [164]: n = m()

In [165]: n.test1 = 10

In [166]: n.test1
Out[166]: 10

Other considerations there:

  • You don't need to talk you are "using metaclasses" for this (though, technically you are) - just say you are creating classes dynamically by calling type. On reading the question title most people will think you are trying to specialize type itself and include __slots__ in it.
  • On the example code you pasted, you have no __init__ method fo your dynamically created classes: so you obviusly won't be able to pass the name parameters on class creation and have them set automatically on the instance, as you do on your explicit class.

If the second topic is your problem, I recomend instead of inheriting of object (implied but he empty - () - second parameter to call), create a baseclass with a generic __init__ method to setup the kwargs as attributes:

class Base:
   __slots__ = ()
   def __init__(self, **kwargs):
       for key, value in kwargs.items():
           setattr(self, key, value)

slotted_class  = type("slotted_class", (Base,), {"__slots__": ...})

Upvotes: 2

Related Questions