Ah, I think I get you. The problem is that code is in the def line but
is only executed when the function is called, is that correct? Because
the code would be "re-executed" just as much if it were written in the
function body. It's executed (at most) once for each call to the
function, just like the ternary's side is.
Hmmm, is that really the root of our disagreement? That's surprising.
:-)
Anyway, yes. The function body and the function signature are two
different things. Function definition time and function call time are
two different things.
I suppose that's a consideration, but it's not nearly as strong in
practice as you might think. A lot of people aren't even aware of the
difference between compilation time and definition time (even people
on this list have made that mistake). Function default args are
executed when the function is defined, not when it's called, and
that's something that changes with this proposal; but there are many
other subtleties to execution order and timing that don't really
matter in practice.
That is a surprising statement to me. I consider the distinction
between definition time and call time to be quite fundamental to Python.
At a minimum one has to understand that the function body isn't
executed when the function is defined (i.e., you have to call it later
to "activate" the defined behavior). This is something I've seen
beginner programmers struggle with.
Understanding the difference between def time and call time is also
important for understanding how decorators work. It's why most
decorators have a structure that is confusing to many newcomers, where
the decorator itself is a function that defines another function and
then returns it. It has to be this way because of the distinction
between the time the decorated function is defined (which is when the
decorator is called) and the time it is called (which is when the "inner
function" that the decorator defines is called).
Or, to put it bluntly, I think people just need to understand the
difference between def time and call time to understand Python's
semantics. Of course even experienced programmers may sometimes get
mixed up in a particular case, but I fully reject the idea that this
distinction is some kind of obscure technicality that we can ignore or
blur by including call-time behavior in the def-time signature. The
distinction is fundamental to how functions work in Python.
Perhaps the key point here is to consider function decorators. We
could avoid them altogether:
def f():
@deco
def g(x): ...
def h(x): ...
h = deco(h)
But as well as having the name replication problem, this buries
important information down in the body of the surrounding code, rather
than putting it at the signature of g/h where it belongs. Even though,
semantically, this is actually part of the body of f, we want to be
able to read it as part of the signature of g. Logically and
conceptually, it is part of the signature. Now compare these two:
Decorators aren't part of the signature of the function they decorate.
deco is not part of the signature of g. A decorator is sort of like a
"transform" applied to the function after it is defined. It's true we
have decorators and like them because they allow us to put that
transform up front rather than at the bottom, but that's not because of
anything about signatures. It's just because some transforms are nice
to know about up front.
def f2():
_SENTINEL = object()
def g(x=_SENTINEL):
if x is _SENTINEL: x = []
...
def h(x=>[]):
...
Which one has its signature where its signature belongs? Yes,
semantically, the construction of the empty list happens at function
call time, not at definition time. But what you're saying is: if there
are no args passed, behave as if a new empty list was passed. That's
part of the signature.
Again, I disagree. Any behavior that happens at call time is not part
of the signature. The signature specifies the function's parameter
names and optionally maps any or all of them to objects to be used in
case they are not passed at call time.
In neither case will you find an object representing the expression []
in the function's signature, because that's not an object, it's an
instruction to build an empty list. In the case of g, you can find a
meaningless and useless object stashed away in __defaults__, but that
doesn't tell you anything about the true behaviour of the function. At
least in the case of h, you can find the descriptive string "[]"
stashed there, which can tell a human what's happening.
You say "In the case of g, you can find a meaningless and useless
object stashed away in __defaults__, but that doesn't tell you anything
about the true behaviour of the function." Yes. And that's fine. As
I've mentioned in other messages in these threads, I am totally fine
with the idea that there is stuff in the function signature that does
not completely characterize the function's behavior. The place for the
function author to explain what the functions defaults mean is in
documentation, and the place for the user to learn what the defaults
mean is also the documentation.
I think again that another area of our disagreement is the relationship
between the function signature and "the true behavior of the function".
To me the signature is to the "true behavior" as the cover of a thick
tome to its contents. The "true behavior" of a function can be
arbitrarily complex in myriad ways which we can never dream of
communicating via the signature. The signature cannot hope to even
begin to convey more than an echo of a whisper of a shadowy reflection
of "the true behavior of the function". We should not worry ourselves
in the slightest about the fact that "the true behavior of the function"
may not be transparently represented in the signature. Sure, we should
choose good names for the parameters, but that's about all I expect from
a function signature.
--
Brendan Barnwell
"Do not follow where the path may lead. Go, instead, where there is no
path, and leave a trail."
--author unknown
_______________________________________________
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/4P5DQMPJHYWXTMH7JEEWJQESBFEXI773/
Code of Conduct: http://python.org/psf/codeofconduct/