Hello,

On Tue, 22 Dec 2020 18:27:01 +1100
Steven D'Aprano <st...@pearwood.info> wrote:

> On Mon, Dec 21, 2020 at 04:36:21PM +0300, Paul Sokolovsky wrote:
> > Hello,
> > 
> > I posted on python-dev a question regarding builtin vars()
> > vs .__dict__ attribute dichotomy:
> > https://mail.python.org/archives/list/python-...@python.org/thread/JAFIBBUU5UE7VMX3SFYXQOHNK6TDZBV3/
> > 
> > It could as well be one among the earliest cases of the violation of
> > "There should be one-- and preferably only one --obvious way to do
> > it."  
> 
> Given that:
> 
> (1) `vars` predates the Zen of Python (vars goes back to at least 
> Python 1.4 in 1996; the Zen dates back to 1999);
> 
> (2) and `vars` *is* the "one obvious way" to access an object's
> internal namespace (dunder names are reserved for the Python
> interpreter; the public API to access an object's symbol table is
> `vars(obj)`)

Thanks for the reply and these points, but the point for .__dict__ is
missing. And I don't know if I made my preference see-thru enough,
but .__dict__ is what I hate ;-).

> I don't think that it counts as a violation. But even if it does,
> well, the Zen is mostly intended to be light-hearted, almost a joke,
> and not as gospel; we take the koans overly seriously at our peril.

Sure, but even if the koan of Python appeared after vars(), it's
common sense that such koans don't appear all at once and represent
U-turn in how things are done. Vice-versa, they appear to capture
existing practices. Which gets us back to the question - which of
vars() vs .__dict__ is "more primary" and why they managed to coexist
for so long, without anyone doing something to that duality
(successfully at least).

And note that I'll definitely end up grepping the old commits, but I
don't rush with that so far, because I know that old commits are
usually not detailed enough - there're likely will be big code drop
with both of them, that's it. And I actually more interested not to
establish an objective fact (vars() appeared 199X-XX-XX, .__dict__ on
199Y-YY-YY), but what old-timers have in memory about that, including
any attempts to address that situation.

> (Seriously, how much *more obvious* can we get than a builtin
> function? You don't have to import it, it is always available.)

That's true, and a few StackOverflow questions in first google hits are
quite unanimously suggest vars(). The question is again what's in
minds of majority of Python users though. 

> On the other hand, *obvious* is not the same as *well known*. I
> expect that many people aren't aware of, or forget, the existence of
> `vars` and use `obj.__dict__` directly. That's so common that I
> expect we have no hope of making `__dict__` a *private*
> implementation detail, even though not all objects, or even all
> classes, have a `__dict__`.

Well, that's why I in the original python-dev post wrote "CPython 10".
But there's another side of the story - various subsets and dialects.
E.g., what Brighton https://brython.info/ (random example, I don't know
its state/stance re: that) should implement, assuming they have an
ambition to implement "only the right one" (as you write programs for
such impls from scratch anyway, or *port* existing code)?

> > Such earlier, that it could as well turn out that this principle
> > was never thoroughly followed by Python at all.
> > 
> > Anyway, how the question pops up, is that there's very rare when
> > there's a need to access *writable* object namespace as dictionary.
> > One of such cases is executing code in a module context, where you
> > need to pass module's globals to exec(), and so I wonder what's
> > "the most canonical way" of getting that - var(mod) or
> > mod.__dict__.  
> 
> I expect the most common way is `globals()`.

globals() returns current module's globals. I was talking on how to
execute code in the context of another module (such a task is mundane
and avoidable in import handling, e.g. writing custom importers).

The only way to switch globals to another module is to execute a
function from it. But we start with empty module and try to bootstrap
it when we perform an import. Actually wait, we can stuff a "bootstrap"
function in a module just to switch to the module context, and then
execute code in now-current context with something like:

mod.__init__ = lambda source: exec(source)
mod.__init__(source)
del mod.__init__


That's why such discussions are useful - I wouldn't think about that
possibility unless I elaborated counter-arguments to what you wrote ;-).

> > But thinking about it, the problem lies on the exec()'s side. If
> > exec could take a module object directly as its "globals" param,
> > there wouldn't be a need to expose internal namespace
> > implementation of the module object.  
> 
> I expect that there is plenty of third-party code that expects to be 
> able to access `module.__dict__` either directly or via `vars`, since 
> that is something that has been documented as working for many
> releases.

Yes, but the question for what they use it. Above, it seemed that access
to module's namespace dict is unavoidable to perform an import and yet
(relatively elegant) workaround was found. __dict__/vars is clearly
needed for reflection, but there're tons of programs can be written
without reflection (as all languages which lack it suggest).

It's all about the question "What is the core of Python?"
https://snarky.ca/what-is-the-core-of-the-python-programming-language/
In this regard, neither vars() nor .__dict__ appear to be in that core,
as even such an advanced matter as executing code in another module
context can be done without them.

> > So, I wonder what old-timers would think of the following signature:
> > 
> > exec(object[, globals_or_module[, locals]])  
> 
> I don't hate it, I just don't see any advantage to adding such 
> complexity to the signature just for the sake of avoiding a function 
> call or dot lookup.

Per above, it actually would be simplification, as you can drop vars()
and .__dict__ from language and still perform import handling. But yeah,
looks that I don't need to change anything on the Pycopy side right
away, and better explore that "switch-to-module-and-exec" idiom above.

> -- 
> Steve


-- 
Best regards,
 Paul                          mailto:pmis...@gmail.com
_______________________________________________
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/4YUJPFVWGT2BFYZLD2YGJCC4YY2YKD4X/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to