[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