4

I've just recently found this weird Python 'bug' and I wanted to see if anyone knew more about it!

for instance take the python module:

import random

class SaySomething:
    def __init__(self, value=random.randint(1, 3)):
        if value == 1: print 'one'
        elif value == 2: print 'two'
        elif value == 3: print 'three'

a = 0

while a < 10:
    SaySomething()
    a += 1

This code will for some reason print the SAME number 10 times!!! Now this I DO NOT understand. It seems that the constructor is being called with the same values 10 consecutive times. But if you print each SaySomething() you'll see that they all have different pointer addresses, so they aren't the same object.

Now if you change:

SaySomething()

to

SaySomething(random.randint(1, 3))

It runs as expected, with actual random choices being made.

Anyone know why this happens?

Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
Parad0x13
  • 2,007
  • 3
  • 23
  • 38
  • 5
    The [default argument](http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument) issue strikes again! – DSM Aug 26 '12 at 02:18
  • Wow, sucha fast and useful bunch of responses here! I wish I could say you all answered my question! Thanks to everyone who helped out, makes a LOT of sense now. – Parad0x13 Aug 26 '12 at 02:32
  • possible duplicate of [Python constructor does weird things with optional parameters](http://stackoverflow.com/questions/2899643/python-constructor-does-weird-things-with-optional-parameters) – Karl Knechtel Aug 26 '12 at 03:25

4 Answers4

13

The problem is that the default arguments in Python are evaluated once, when the function is created. To fix this, try:

    def __init__(self, value = None):
        if value is None:
             value = random.randint(1, 3)

        if value == 1: print 'one'
        elif value == 2: print 'two'
        elif value == 3: print 'three'

This way, we shift the randomization into the function itself, instead of at the time of function definition.

Alexander Kondratskiy
  • 4,156
  • 2
  • 30
  • 51
4

In python, default arguments are initialized once. So you are getting the same value over and over, because that was the value when the default argument was initialized. See http://www.deadlybloodyserious.com/2008/05/default-argument-blunders/

André Oriani
  • 3,553
  • 22
  • 29
3

It's because when the class is compiled or interpreted the value of variable value is set to random.randint(1, 3), and it doesn't changes until you pass some other value to it.

Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
1

For the same reason as in Python constructor does weird things with optional parameters

the default value of an optional argument is a single instance and thus is only calculated once for all instances being made.

To fix this:

import random 
class saySomething: 
    def __init__(self, value = None):
        random_value = random.randint(1, 3) if value == None else value
        if random_value in [1, 2, 3] print ['one', 'two', 'three'][random_value - 1]

for a in xrange(10):
    saySomething() 
Community
  • 1
  • 1
Michel Keijzers
  • 15,025
  • 28
  • 93
  • 119
  • 1
    You could also do a closure, as in `def __init__(self, value=lambda: random.randint(1, 3)):`, but it doesn't make much sense for this situation ;) – Joel Cornett Aug 26 '12 at 02:40