10

In Python, the __dict__ of an instance seems to be "open" for some classes, "closed" for others:

# "closed" type example:
x = object()
x.eggs = 'spam'   # raises an AttributeError

whereas:

# "open" type example I:
x = Exception()
x.eggs = 'spam'   # does not raise an AttributeError

# "open" type example II: subclasses
class thing(object): pass
x = thing()
x.eggs = 'spam'   # does not raise an AttributeError

I would like to understand where/how/why this difference in behaviour is implemented: why is object closed but thing open? Why is Exception open? I have two reasons for the question:

  • In pure Python, the only way I have found of replicating the "closed" behaviour is to implement __setattr__ myself and raise the AttributeError in it, composing its appropriately-formatted error message by hand. Is there a less-verbose, more-DRY way (and one which doesn't generate a potentially misleading backtrace to __setattr__)?

  • In an IPC module I have written, I want to document an example of usage, and the most concise and universal way would be to find an example of a builtin type (failing that, one from the standard library) (a) which can be pickled and sent to another process, and (b) whose instances exhibit the "open" behaviour. So far the best one I have found is Exception but that looks a little misleading in the documentation (people will think it's specifically about exceptions). A more generic object would be nicer, but object is closed, and a subclass (like thing in the example above) is not pickleable unless you save its definition in a file on the path. Maybe there's a standard container somewhere that's open? The classics (tuple/list/dict) are all closed.

jez
  • 14,867
  • 5
  • 37
  • 64
  • Built-ins (the *real* ones, i.e. those directly written in C) don't support attribute assignment. The point is: **builtins don't have a `__dict__`!**. For example: `object().__dict__ -> AttributeError: 'object' object has no attribute '__dict__'`. – Bakuriu Jul 05 '16 at 19:16
  • `x = object()` has no `__dict__`? – Moses Koledoye Jul 05 '16 at 19:16
  • 3
    See [`__slots__`](https://docs.python.org/2/reference/datamodel.html#slots) – Cristian Ciupitu Jul 05 '16 at 19:16
  • @Bakuriu I wondered about that, but I think the implementation of `Exception` is in C too. – jez Jul 05 '16 at 19:17
  • You could try using `collections.namedtuple` ? – GWW Jul 05 '16 at 19:22
  • 1
    Also a dupe of http://stackoverflow.com/q/1529002/748858 – mgilson Jul 05 '16 at 19:23
  • As a mater of fact attribute assignment updates the module's namespace dictionary, for example `my_obj.sample_attr = 'my_value'` is equivalent to `my_obj.__dict__["sample_attr "] = 'my_value'`. Also note that class attribute assignments update the class’s dictionary, never the dictionary of a base class. Read more https://docs.python.org/3/reference/datamodel.html – Mazdak Jul 05 '16 at 19:23
  • @Kasramvd -- Well ... that's only true for objects that have a `__dict__`. For objects that define `__slots__`, it is a little more tricky. – mgilson Jul 05 '16 at 19:33
  • @CristianCiupitu Thanks: understanding of `__slots__` is exactly what I was lacking (I somehow never heard of it in 8 years of Python). – jez Jul 05 '16 at 19:36
  • @mgilson Yes exactly. Basically I think such adversative behaviors are not very correct and may cause some problems and confusions. – Mazdak Jul 05 '16 at 19:43
  • @GWW: `namedtuple` turns out not to be a `type`, it's a factory function. Incidentally I found that Python 3.3+ has `types.SimpleNamespace` for exactly this purpose, but unfortunately that doesn't help as I want to support Python 2 as well. – jez Jul 05 '16 at 19:47
  • @jez: The object returned by the namedtuple factory function has the `__dict__` attribute and you can add additional attributes after. Perhaps the underlying type generated by the factory function can be used for your purposes. – GWW Jul 05 '16 at 22:09

0 Answers0