On 14 December 2016 at 10:40, Steven D'Aprano <st...@pearwood.info> wrote: > On Wed, Dec 14, 2016 at 12:12:39AM +0000, Emanuel Barry wrote: >> > From Steven D'Aprano >> > Sent: Tuesday, December 13, 2016 6:49 PM >> > To: python-ideas@python.org >> > Subject: Re: [Python-ideas] Enhancing vars() >> > >> > But if the object has __slots__, with or without a __dict__, then vars >> > should return a proxy which direct reads and writes to the correct slot >> > or dict. >> > >> > It might be helpful to have a slotsproxy object which provides a >> > dict-like interface to an object with __slots__ but no __dict__, and >> > build support for both __slots__ and a __dict__ on top of that. >> >> That might be a bit tricky, for example, it's possible that a class has a >> `foo` slot *and* a `foo` instance attribute (by virtue of subclasses). What >> would you do in that case? > > vars() shouldn't need to care about inheritance: it only cares about the > object's own individual namespace, not attributes inherited from the > class or superclasses. That's how vars() works now: > > py> class C: > ... cheese = 1 > ... > py> obj = C() > py> ns = vars(obj) > py> 'cheese' in ns > False > > The only difference here is that if the direct parent class has > __slots__, the instance will use them instead of (or in addition to) a > __dict__. We don't need to care about superclass __slots__, because they > aren't inherited.
If folks genuinely want an attrproxy that provides a dict-like view over an instance, that's essentially: from collections import MutableMapping class AttrProxy(MutableMapping): def __init__(self, obj): self._obj = obj def __len__(self): return len(dir(self._obj)) def __iter__(self): for attr in dir(self._obj): yield attr def __contains__(self, attr): return hasattr(self._obj, attr) def __getitem__(self, attr): return getattr(self._obj, attr) def __setitem__(self, attr, value): setattr(self._obj, attr, value) def __delitem__(self, attr): delattr(self._obj, attr) >>> class C: ... a = 1 ... b = 2 ... c = 3 ... >>> ns = AttrProxy(C) >>> list(ns.keys()) ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'c'] >>> ns["d"] = 4 >>> C.d 4 >>> C.c 3 >>> del ns["c"] >>> C.c Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: type object 'C' has no attribute 'c' >>> ns["a"] = 5 >>> C.a 5 Replacing the calls to `dir(self._obj)` with a method that filters out dunder-methods and updating the other methods to reject them as keys is also relatively straightforward if people want that behaviour. Indirecting through dir(), hasattr(), getattr(), setattr() and delattr() this way means you don't have to worry about the vagaries of the descriptor protocol or inheritance or instance attributes vs class attributes or anything else like that, while inheriting from MutableMapping automatically gives you view-based keys(), values() and items() implementations. I wouldn't have any real objection to providing an API that behaves like this (in simple cases its functionally equivalent to manipulating __dict__ directly, while in more complex cases, the attrproxy approach is likely to just work, whereas __dict__ manipulation may fail). (Re-using vars() likely wouldn't be appropriate in that case though, due to the change in the way inheritance is handled) I *would* object to a new proxy type that duplicated descriptor logic that's already programmatically accessible in other builtins, or only selectively supported certain descriptors (like those created for __slots__) while ignoring others. Pseudo-lookups like inspect.getattr_static() exist to help out IDEs, debuggers and other code analysers, rather than as something we want people to be doing as part of their normal application execution. Cheers, Nick. -- Nick Coghlan | ncogh...@gmail.com | Brisbane, Australia _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/