On Mon, Aug 4, 2014 at 12:36 AM, Roy Smith <r...@panix.com> wrote: > In article <mailman.12582.1407072928.18130.python-l...@python.org>, > Chris Angelico <ros...@gmail.com> wrote: > >> On Sun, Aug 3, 2014 at 11:25 PM, Roy Smith <r...@panix.com> wrote: >> > in which case, I've said, "make Foos just like objects, except for, oh, >> > never mind, there aren't any differences". But, in reality, the system >> > bolted on the ability to have user-defined attributes without telling >> > me. I don't think it's unreasonable to be surprised at that. >> >> I agree that this is slightly surprising. However, imagine if it were >> the other way: >> >> class Foo(object): >> x = 1 >> def __init__(self): self.y = 2 >> >> These would throw errors, unless you explicitly disable __slots__ >> processing. When there's two ways to do things and both would be >> surprising, you pick the one that's going to be less of a surprise, or >> surprising less often, and accept it. That doesn't mean it's not a >> problem, but it's better than the alternative. >> >> ChrisA > > I'm not following you at all. What does "the other way" mean, and how > would that cause the above to generate errors, and what does this have > to do with __slots__?
Fact #1: Some classes, like object, don't have a dictionary for arbitrary attributes. (I'll call this __slots__ mode, although it's not technically __slots__when it's a C-implemented class.) Fact #2: You can subclass such objects. There are two ways that this subclassing could be done. Either it maintains __slots__ mode, which means you can see every change (and going "class Foo(Bar): pass" will make an exact subclass of Bar with identical behaviour), or it drops __slots__ and adds a dictionary unless you explicitly reenable __slots__. >>> class Base(object): __slots__ = ('a', 'b', 'c') >>> class Deriv(Base): pass >>> Base().d = 1 Traceback (most recent call last): File "<pyshell#71>", line 1, in <module> Base().d = 1 AttributeError: 'Base' object has no attribute 'd' >>> Deriv().d = 1 Python opted to go with the second behaviour: the subclass is not bound to the superclass's __slots__, but gets a dictionary unless it itself specifies __slots__ (in which case it gets all of them, parent's included): >>> class SlottedDeriv(Base): __slots__ = ('d', 'e', 'f') >>> SlottedDeriv().a = 1 >>> SlottedDeriv().d = 1 >>> SlottedDeriv().g = 1 Traceback (most recent call last): File "<pyshell#79>", line 1, in <module> SlottedDeriv().g = 1 AttributeError: 'SlottedDeriv' object has no attribute 'g' The alternative would be for __slots__ to normally copy down, and to have to be explicitly removed - for "pass" to be actually equivalent to this: >>> class Deriv(Base): __slots__ = Base.__slots__ >>> Deriv().d = 1 Traceback (most recent call last): File "<pyshell#82>", line 1, in <module> Deriv().d = 1 AttributeError: 'Deriv' object has no attribute 'd' and for some other syntax to do what "pass" currently does. That has the benefit of purity (it's easy to describe what happens, there's no magic going on), but at the expense of practicality (you'd have to explicitly de-slottify your classes before you can add functionality to them). And we know what the Zen of Python says about which of those takes priority. ChrisA -- https://mail.python.org/mailman/listinfo/python-list