2

In the following code, a random value is generated as expected:

import random

for i in range(10):
    print(random.randint(0,10))

However, this does not work if I use a function:

import random

def f(val: int = random.randint(0,10)):
    print(val)

for i in range(10):
    f()

Why is the result of the second code snippet always the same number? The most similar question I could find is this one, but it refers to a different language (I don't master) .

mushishi
  • 141
  • 1
  • 10
  • 1
    It's because arguments are evaluated only once. You can search "python default arguments" for more. – Asocia Jun 16 '20 at 16:12
  • 2
    Does this answer your question? [“Least Astonishment” and the Mutable Default Argument](https://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument) – MisterMiyagi Jun 16 '20 at 16:12
  • @MisterMiyagi it did lead me to this article: http://effbot.org/zone/default-values.htm , which answers my question. But by the time I return to the thread, there are answers already. Thank you for linking your suggested read – mushishi Jun 16 '20 at 16:26

3 Answers3

6

The default argument expression isn't evaluated when you call the function, it's evaluated when you create the function. So you'll always get the same value no matter what you do.

The typical way around this is to use a flag value and replace it inside the body of the function:

def f(val=None):
    if val is None:
        val = random.randint(0,10)
    print(val)
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • Chose this as the answer because it states when/how **val** is evaluated. I understand it is like a _static_ local variable in C – mushishi Jun 16 '20 at 16:30
  • @mushishi I always try to emphasize that point, because I'm familiar with C++ which does it the other way around - default arguments are evaluated at call time. Calling it a *static* isn't quite accurate, rather the function `f` becomes an object and the argument list and defaults become attributes of that object. – Mark Ransom Jun 16 '20 at 16:53
1

You'll want to have the default value be a specific value. To make it be dynamic like that, you'll want to default it to something else, check for that, and then change the value.

For example:

import random

def f(val=None):
    if val is None:
        val = random.randint(0,10)
    print(val)

for i in range(10):
    f()
Ken Kinder
  • 12,654
  • 6
  • 50
  • 70
0

The default param can't be changed on calling. I can't understand why it needed. you can do simply like this.

import random
def f():
  print(random.randint(0,10))
for i in range(10):
  f()
dauren slambekov
  • 378
  • 3
  • 15
  • This is an oversimplification from real-world code. I could check whether the default argument is set and if no, return; but I was curious why I cannot save myself the extra couple of lines – mushishi Jun 16 '20 at 16:28