Dealing with the __str__ method in classes with lots of attributes
Karl Knechtel
zahlman at gmail.com
Mon May 14 13:15:28 EDT 2012
On Sat, May 12, 2012 at 9:10 AM, Ethan Furman <ethan at stoneleaf.us> wrote:
>
> Firstly, __slots__ is a tuple.
I object: conceptually, the "slots" of a class are set in stone, but
the `__slots__` attribute of a class object is just an attribute, and
any iterable (as long as it yields valid identifier names) can be used
when the `__slots__` magic is invoked in the class definition. FWIW,
all the ordinary examples I've seen use a list, although a tuple
arguably makes more sense.
Observe:
>>> class Evil(object):
... __slots__ = ('a%s' % a for a in range(10))
...
>>> Evil().__slots__
<generator object <genexpr> at 0x01EDFAA8>
>>> list(Evil().__slots__)
[] # the generator was consumed by the metaclass during class creation
>>> dir(Evil())
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__has
h__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__rep
r__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '
a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'a9'] # yep, the
expected attributes are there, and can be set.
>>> Evil.__slots__ = 42
# no exception
>
> Secondly, this is bad advice. __slots__ is there as a memory optimization
> for classes that will have thousands of instances and do not need the
> ability to have arbitrary attributes added after creation.
I did explicitly indicate the latter part.
> __slots__ is an
> advanced feature, and as such is easier to get wrong. It is *not* there to
> make __str__ nor __repr__ easier to write.
Of course not, but it seems to me that it serves well in this particular case.
>>>> class Test1(object):
> ... __slots__ = ('item', 'size')
> ... desc = 'class-only attribute'
> ...
>
>>>> t1 = Test1()
>>>> t1.desc = 'read-only attribute' # fails because 'desc' is
> # not in __slots__
Well, sure; expecting to modify a class attribute via an instance is a
bit naughty anyway.
>>>> print t1.item # fails because 'item' was
> # not set
Well, yes, that's what `__init__` is for; the same line would fail
without `__slots__` and for the same reason. Arguably, this is a
gotcha for people coming from C-like languages who are expecting
`__slots__` to make the class behave as if it had a defined layout,
but there isn't actually any more work involved here.
>>>> class Test2(Test1):
> ... def __init__(self, price):
> ... self.price = price
> ...
>>>> t2 = Test2(7.99) # __slots__ not defined in
> # subclass, optimizations lost
Well, yes, but we aren't using it for the optimizations here!
But I see your point; explicit is better than implicit, and our
explicit purpose here is to have an explicit list of the attributes
we're interested in for __str__/__repr__ - which could be any other
named class attribute, without magic associated with it. That said,
`__slots__` is as close to a canonical listing of instance-specific
attributes as we have (`dir()` clearly won't cut it, as we don't want
methods or other class-specific stuff).
--
~Zahlman {:>
More information about the Python-list
mailing list