5 ms·
Can you clarify? def foo(default_arg = []): Why can't that just be shorthand for: def foo(default_arg = ParamNone): if default_arg == ParamNo
by jcampbell1 12y ago
Can you clarify?
def foo(default_arg = []):
Why can't that just be shorthand for:
def foo(default_arg = ParamNone):
if default_arg == ParamNone:
default_arg = []
How would that break first class functions?
- bkeroack 12y agoHow would the above work for duck-typed lists, for example? How would the language "know" which types are mutable?
- dalke 12y agoAs a minor point, use "default_arg is ParamNone", since "==" probably won't do the right thing. What breaks is something like: def foo(default_arg = slow_f()): pass Under the shorthand gets turned into: ParamNone = object() def foo(default_arg = ParamNone): if default_arg is ParamNone: default_arg = slow_f() pass This is fine, since everyone would know that the shorthand means to not put slow code there. Instead, people will start writing it as: _foo_arg = slow_f() def foo(default_arg = _foo_arg): pass Of course, then what happens with: _foo_arg = slow_f() def foo(default_arg = _foo_arg): _foo_arg = 5 ? Under expansion it becomes: _foo_arg = slow_f() def foo(default_arg = ParamNone): if default_arg is ParamNone: default_arg = _foo_arg _foo_arg.add(5) This violates Python's scoping rules, because _foo_arg is now being used in local scope instead of global scope. Eg: >>> def f(x=None): ... if x is None: ... x = spam ... spam = 3 ... >>> spam = 9 >>> >>> f() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in f UnboundLocalError: local variable 'spam' referenced before assignment Which means you now need a new scoping rule, just to handle default parameters without making things more confusing. It also turns what was a simple O(1) offset into a precomputed list into a globals() lookup for many cases.
- syllogism 12y agoIn Python 3 there's the nonlocal keyword to deal with the scoping thing. The default arguments thing is worse than a lot of the stuff Python 3 corrected.
- dalke 12y agoI don't see how nonlocal could fix this. Could you explain? More specifically, given a better 'default arguments thing', how would you interpret: x = [2] def f(x=x*5): x.append(4) With the earlier conversion it's: x = [2] def f(x=DefaultArg): if x is DefaultArg: x = x*5 x.append(4) This isn't going to work because the x inside of f() is different than the outside x, and you'll get the error message I mentioned. If you add a nonlocal, as in: x = [2] def f(x=DefaultArg): nonlocal x if x is DefaultArg: x = x*5 x.append(4) then you'll get "SyntaxError: name 'x' is parameter and nonlocal". What other solution are you thinking of?
- syllogism 12y agoAh I see. Yeah nonlocal can't fix that.
- ollysb 12y agoWhy not just memoize the result of the slow function?
- dalke 12y agoI think by your use of "just memoize" that I didn't explain myself well enough. I don't mean to focus on the slow aspect, nor even the function aspect. It could be def foo(default_arg = [0]*(256*256)): ... and still get the same namespace issues. Memoization is not always going to be an available solution. For example, it may be that slow_f() returns a stateless object, so can be reused, while slow_f(x) returns something stateful. You can think of my examples as either using default arguments as a single element memo, or using a module variable for the same. Both premised on the idea that the developer knows enough to make the right decision.