7 ms·
I'm not a Python expert, but iirc from various blog posts the "l" variable does not get reset between function calls which will cause undesired behavior. So ca
by trevor-e 12y ago
I'm not a Python expert, but iirc from various blog posts the "l" variable does not get reset between function calls which will cause undesired behavior. So calling the function 3 times without argument would produce a list of size 1,2, and 3 with the third call rather than 3 lists of size 1. Can any Python guru's confirm?
- ryangittins 12y agoYes, that's correct. The default value is only interpreted once, when the `def` statement is called. After that point, it's completely mutable. You have to see Python functions as objects and default parameter values as object variables.
- Dylan16807 12y agoThe problem is not the existence of the object variables, but that they are in such an unfortunate place. The rest of the parameter list is declaring fresh local variables. It's inconsistent that the left side of the equals sign is per-invocation, and the right side is per-def.
- Genmutant 12y agoYes that is correct. The default value gets created when the function is interpreted ("compiled").
- deathanatos 12y ago> The default value gets created when the function is interpreted ("compiled"). No. The default value gets "created" (the expression is evaluated and stored) when the def statement is executed. Take the following example: In [1]: def foo(): ...: def append_five(l=[]): ...: l.append(5) ...: return l ...: return append_five ...: In [2]: a = foo() In [3]: b = foo() In [4]: a() Out[4]: [5] In [5]: b() Out[5]: [5] In [6]: _4 is _5 Out[6]: False We only wrote one function definition, but multiple lists are created. (They are created when the "def append_five" definition executes, during the execution of foo.)
- tmerr 12y agoI thought that was what he meant. Is there any sharp distinction between "interpreting" and "evaluating" in python that I am unaware of? I've always used the words more or less interchangeably. But now that I think about it that might be a little naive since I have no idea how it works under the hood
- gcr 12y agoThe parent wrote "compiled", which is certainly more incorrect than either "interpreted" or "evaluated."
- msellout 12y agoYou could say that interpreting is first parsing and second executing/evaluating. The parser tokenizes and does a small amount of optimization such as ignoring unassigned values.
- SEJeff 12y agoThe key is object mutability. A list type is mutable and a tuple type is immutable. If the candidate correctly deduces what will happen, I'll ask them to write a bug-free version, which looks like one of the below: def append_one(var=None): var = var or [] var.append(1) return var def append_one(var=None): if var is None: var = [] var.append(1) return var Mutability is a very subtle but very important concept to understand in python. Everyone who uses python for non-trivial code should know it well: https://docs.python.org/2/reference/datamodel.html https://docs.python.org/2/reference/datamodel.html
- mbell 12y ago> The key is object mutability. A list type is mutable and a tuple type is immutable. I don't think the question has much to do with mutability, it isn't surprising to me nor would I imagine most programmers that a list is mutable, that's very common. The surprising part of this question is that the default value of 'l' continues to exist outside the lexical scope of the function, the expected behavior is that the value of 'l' is initialized at function call time and is garbage collected after each call. As it sits, using default values in python is sort of like defining a global that only has a named reference inside the function block, which is very strange.
- pyre 12y agoIt has something to do with mutability, because if an object is immutable, the behavior of Python matches what the naive developer expects. It's only mutable objects that break those expectations. Don't even get into unexpected behavior in classes: In [1]: class A(object): ...: l = [] ...: In [2]: a, b = A(), A() In [3]: a.l.append("Something") In [4]: a.l Out[4]: ['Something'] In [5]: b.l Out[5]: ['Something'] In [6]: class B(object0: ...: KeyboardInterrupt In [6]: class B(object): ...: l = None ...: def __init__(self): ...: self.l = [] ...: In [7]: c, d = B(), B() In [8]: c.l.append("Something") In [9]: c.l, d.l Out[9]: (['Something'], [])
- 12y ago
- zo1 12y agoThe expression presented in the parameter list is only evaluated once, and that is when the method is defined. The confusion is that people assume the expression is evaluated every time the method is called.
- kyllo 12y agoThe confusion is that people assume the expression is evaluated every time the method is called. Because that's how it works in a lot of other languages, such as Ruby and Javascript.
- zo1 12y agoI doubt that's the only reason. I fell for it myself at first without ever seeing a line of Ruby. It initially feels intuitive, and that's why I think most fall for it.
- ollysb 12y agoI think it's because the arguments are bound when the function is called. It's just natural that you'd expect the default values to also be bound at the same time.