[Python-ideas] Enhancing vars()
Steven D'Aprano
steve at pearwood.info
Mon Dec 12 18:45:03 EST 2016
In general, directly accessing dunders is a bit of a code smell. (I
exclude writing dunder methods in your classes, of course.) There's
usually a built-in or similar to do the job for you, e.g. instead of
iterator.__next__() we should use next(iterator).
One of the lesser-known ones is vars(obj), which should be used in place
of obj.__dict__.
Unfortunately, vars() is less useful than it might be, since not all
objects have a __dict__. Some objects have __slots__ instead, or even
both. That is considered an implementation detail of the object.
Proposal: enhance vars() to return a proxy to the object namespace,
regardless of whether said namespace is __dict__ itself, or a number of
__slots__, or both. Here is a woefully incompete and untested prototype:
class VarsProxy(object):
def __init__(self, obj):
if not (hasattr(obj, '__dict__') or hasattr(obj, '__slots__')):
raise TypeError('object has no namespace')
self._obj = obj
def __getitem__(self, key):
slots = getattr(type(self), '__slots__', None)
# see inspect.getattr__static for a more correct implementation
if slots is not None and key in slots:
# return the content of the slot, without any inheritance.
return getattr(self._obj, key)
else:
return self._obj.__dict__[key]
def __setitem__(self, key, value): ...
def __delitem__(self, key): ...
One complication: it is possible for the slot and the __dict__ to
both contain the key. In 3.5 that ambiguity is resolved in favour of the
slot:
py> class X:
... __slots__ = ['spam', '__dict__']
... def __init__(self):
... self.spam = 'slot'
... self.__dict__['spam'] = 'dict'
...
py> x = X()
py> x.spam
'slot'
Although __slots__ are uncommon, this would clearly distinguish
vars(obj) from obj.__dict__ and strongly encourage the use of vars()
over direct access to the dunder attribute.
Thoughts?
--
Steve
More information about the Python-ideas
mailing list