On Wed, Dec 30, 2020 at 5:01 PM Steven D'Aprano <st...@pearwood.info> wrote:

> On Tue, Dec 29, 2020 at 09:02:10AM -0800, Guido van Rossum wrote:
> > On Tue, Dec 29, 2020 at 8:11 AM Steven D'Aprano <st...@pearwood.info>
> wrote:
>
> > To the contrary, vars() is something I added to the language for the
> > benefit of REPL users (like dir()), and other usages look suspect to me.
> I
> > find that using `__dict__` is more direct about the purpose, and also it
> is
> > the prevailing style.
>
> I cannot argue with your historical perspective on this, and I agree
> that `vars()` is not as well known as I believe it should be.
>
> So you are right on the bare facts. I still think you are wrong on the
> aesthetics :-)
>
> Your comment also shines some light on why `vars()` with no argument
> returns `locals()`, which otherwise seems strange to me.
>

This is the first hint that vars() is not what it seems.

Nevertheless, I have to ask what could possibly be "suspect" about using
> vars() programmatically? It isn't like dir().
>

But it is dir()'s cousin, and the fact that its meaning hasn't changed
doesn't mean they couldn't: Christopher Barker is already asking for such a
change. This is the second hint.


> dir() is certainly a convenience function for interactive use, and is
> documented as returning "the most relevant, rather than complete,
> information".
>
> This note is repeated again later in the docs: "Because dir() is
> supplied primarily as a convenience for use at an interactive prompt, it
> tries to supply an interesting set of names more than it tries to supply
> a rigorously or consistently defined set of names, and its detailed
> behavior may change across releases."
>
> On the other hand, `vars()` has no such warnings. There's no wiggle-
> room: it either returns the instance `__dict__` or it raises TypeError.
> So aside from the difference in exceptions (AttributeError versus
> TypeError) I don't think that there is any possible difference between
> direct attribute access and the output of vars(). Am I wrong?
>

That's just because vars() didn't turn out to be so useful, or perhaps
because people didn't think of improving it when dir() was improved. I
suspect that if you look at the original docs, dir() and vars() had pretty
similar documentation. (And according to PEP 361, `__dir__` was originally
added in 3.0 -- though it seems to have been backported to 2.x at some
point.)


> Speaking of slots, I've often been annoyed that there is no abstraction
> that hides the difference between instances that use a dict as symbol
> table, and those that use slots. (And those that use both.) If you need
> an object's symbol table, for introspection or otherwise, you're out of
> luck if it uses slots.
>
> There doesn't seem to be any way to handle these two implementations in
> the same way, and objects with both slots and a dict can give surprising
> results if naive code expects `__dict__` to be the symbol table:
>
>
> >>> class A:
> ...     __slots__ = ('spam', '__dict__')
> ...
> >>> obj = A()
> >>> obj.spam = True
> >>> 'spam' in obj.__dict__
> False
> >>> obj.__dict__.update(spam=False)
> >>> obj.spam
> True
>
>
> So there's no way to get an object's symbol table in an implementation-
> independent way. Whether you use `obj.__dict__` or `vars(obj)` it only
> gives you the symbol table for objects that use a dict. There's nothing
> that works for objects that use slots.
>
> So far I've worked around this in an ad-hoc fashion by testing for
> `__slots__` and treating that case as special, but it would be nice to
> ignore the implementation details and just have a "symbol table" object
> to work with. What do you think?
>

That you're providing an excellent argument to evolve vars() independently
from `__dict__`. :-)


> > > Do you prefer to write `mylist.__len__()` over `len(mylist)`? Then you
> > > will probably prefer `obj.__dict__` over `vars(obj)` too :-)
> >
> > Not a valid analogy.
>
> I think it is. Apart from a matter of taste, what part of the analogy
> do you feel is invalid?
>

len() is an important abstraction for containers, and its usage deserves a
short name (just like unary minus and abs() for numbers). This is crucial
even though you have to use its "true name" (https://xkcd.com/2381/) to
define the implementation for a particular class.

OTOH, `__dict__` is the opposite of an abstraction -- whether you spell it
vars(x) or `x.__dict__`, the matter remains that you're looking inside the
implementation of the object, and what you get is not an abstraction -- as
you've pointed out for `__slots__`, and as is also apparent for e.g.
namedtuple or fundamental objects like numbers, strings and built-in
container types.

By making the dominant spelling `__dict__`, we remind people that when they
use this, they're tinkering with the implementation. Don't get me wrong,
this can be fun and useful, but it's not a clean abstraction. The more
fundamental abstraction is getattr()/setattr(), since it is supported by
*all* objects, not just most classes.

-- 
--Guido van Rossum (python.org/~guido)
*Pronouns: he/him **(why is my pronoun here?)*
<http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-change-the-world/>
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/T2RL47EI3NNJ3AOYGHVCWQA4HLDQZ2E5/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to