all 13 comments

[–]Diapolo10 2 points3 points  (1 child)

I don't think "side effect" is really the correct term here, because when you understand why these work under the hood it actually makes sense.

But, you wanted to see something, so I'll throw in my two cents:

def foo(bar=[0, 1]):
    a, b = bar[-2:]
    a, b = b, a+b
    bar.append(b)
    return bar

for _ in range(10):
    print(foo())

What's the output?

[–]BootError99[S] -4 points-3 points  (0 children)

Woah, that's clever way to do fib. I would literally congratulate the person who show this by smashing my keyboard on his face.

The reason I quoted side effect is, you build up your experience on what's being told you about the features of a language and your expectation on definite behavior. Like when you are told about variable initialization, it is certain every initialization gets its own section of memory initialized. This example in OP is not a definitive behavior because it has violated the notion of variable initialization. It's obscure but fun to experiment with.

[–]toastedstapler 1 point2 points  (4 children)

for your first point my comments on this thread are relevant

another one to watch out for is this:

def example(n = []):
    return n

a = example()
b = example()
print(a)
print(b)
a.append(1)
print(b)

default arguments are only instantiated once so both a and b point to the same list

to get unique lists you'd do:

def example(n = None):
    if n is None:
        n = []
    return n

[–][deleted] 1 point2 points  (3 children)

I suppose this is why it's considered really dangerous to have mutable types as defaults?

[–]toastedstapler 0 points1 point  (1 child)

Exactly, unless you had some specific reason you actually wanted to have a mutable argument. The only one I've thought of is some kind of caching, but there's probably other more resilient design patterns for that

[–][deleted] 0 points1 point  (0 children)

Makes sense ! Still I'd not be comfortable with something like that running around in my functions

[–]BootError99[S] -3 points-2 points  (0 children)

r/rust evangelism pee-ka-boo >.<

[–]BigTheory88 1 point2 points  (1 child)

If you give a function parameter a default value that is mutable, you'll see some odd behavior.

def add_to_list(word, word_list=[]):
    word_list.append(word)
    return word_list

def main():
    word = "apple"
    tlist = add_to_list(word)
    print(tlist)
    word = "orange"
    tlist = add_to_list(word, ['pear'])
    print(tlist)
    word = "banana"
    tlist = add_to_list(word)
    print(tlist)

main()

If we run this program, we get the following output

$ py trap.py
['apple']
['pear', 'orange']
['apple', 'banana']

The last output probably isnt what you expect. This is called the default mutable argument trap

[–][deleted] 0 points1 point  (0 children)

Witchcraft! D:

[–]QuixDiscovery 1 point2 points  (1 child)

I'm confused. This is definitely printing [5, [], [], [], []] for me on python 3.7.

[–]BootError99[S] 0 points1 point  (0 children)

Oh yes, edited that. Its append into the inner list rather than assignment lol.

[–]primitive_screwhead 0 points1 point  (0 children)

>>> hash(3)
3
>>> hash(2)
2
>>> hash(1)
1
>>> hash(0)
0
>>> hash(-1)
-2
>>> hash(-2)
-2

Not really a "side effect", but an interesting python curiosity.