On 19/11/2015 12:19, Steven D'Aprano wrote:
On Thu, 19 Nov 2015 10:14 am, BartC wrote:

Consider this pair of functions:


def expensive():
     # Simulate some expensive calculation or procedure.
     time.sleep(100)
     return random.randint(1, 6)


def demo(arg=expensive()):
     return arg + 1


Now we call the second function four times, without an argument so that the
default value is used:

demo()
demo()
demo()
demo()

What results do you expect?

If someone /wants/ expensive() to be called each time, then why not? If they don't, then it seems easy enough to write:

demo_default=expensive()
def demo(arg=demo_default):
        ...

(As you mention later...)

- if the language defaults to early binding, it is *easy* for the
   programmer to get late binding semantics;

- if the language defaults to late binding, it is *very difficult*
   for the programmer to get early binding semantics.

I got the impression that Python was a nice, easy language for everyone to use. Not one where you need a Master's degree in CS to understand the nuances of! And to understand why something that is so blindingly obvious doesn't work.

But let's try going the other way. Suppose function defaults were evaluated
each and every time you called the function. How could you *avoid* the
expense and waste of re-evaluating the default over and over again?

I use default parameters a lot in other languages.

99% of the time the default value is a constant. And most often that constant is 0, "" or an empty list.

You want these very common examples to /just work/ instead of going to lengths trying to explain why they don't.

You can't, or at least, not cleanly and easily. The most obvious way is to
use a global variable:

ARG = expensive()

def demo(arg=ARG):
     ...

This is ... horrible. You are still evaluating the default each time,

I implement an interpreted language where these calls:

  demo()
  demo(ARG)

would have exactly the same cost.

I understand that Python is very different: at the call-site, the compiler has no idea of the number of arguments a function defines or what the default values might be, or even if it is a function that is being called.

But even under those circumstances, I would endeavour to make such a function call as fast as is practical.

(That could involve a runtime check on number of parameters, substituting the equivalent of None for missing ones, or whatever default expression has been declared.

But here, I am benefiting from my language not being Python which does seem to like making things difficult.)

but at
least it is only a global variable lookup,

Maybe you can wrap the entire module inside a function? Other than a bit at the end that calls that function. Does that solve the global lookup problem?

When you deal with mutable objects, you have to expect them to mutate. The
whole point of mutability is that their value can change.

That [] doesn't look like an object that could change. It looks like an empty list constructor. You would expect a constructor for an empty list to yield an empty list throughout a program! (As it does, in most other contexts.)

You presumably think differently because you have some inside knowledge of how Python works, and know that that [] undergoes a one-time assignment to a local, persistent 'default' variable where it's value can indeed by changed. (Thanks to another Python feature where an assignment is a very shallow copy of an object.) And it is that volatile variable that is the actual default.

But not everyone is going to know that.

--
Bartc
--
https://mail.python.org/mailman/listinfo/python-list

Reply via email to