If you say "f(x=[])" (assuming that worked without the actual side effects it has), someone could still say "x(None)" instead of "x()", causing the function to die. Since a robust program isn't able to avoid checking for None, it might as well set defaults there too.
There is another case where this is important; you might want the equivalent of "f(x=expensive_function_to_calculate_useful_default())", and you don't want that function called unless it needs to be. Only the x=None approach allows this to be deferred.
In the expensive case, I'd just calculate it once and store it somewhere (possibly as a lookup dictionary if there are multiple inputs) and access that from within the function.
But None is a result that can happen in situations that would otherwise return exactly the expected type. If "nothingness" can be meaningful (especially in a function that accepts an empty list as a parameter, say), it's nicer if the code just deals with None itself instead of requiring checks for None in all the callers.
> (defvar *fn*
(let ((x 3))
(lambda (&optional (y (list nil x)))
(push 7 (car y)) ; modifies the list
y)))
*FN*
> (funcall *fn*)
((7) 3)
> (funcall *fn*)
((7) 3)
From this example you can see two things. First, the binding of 'x' is closed over when the lambda expression is evaluated. And second, the expression that provides the default value of 'y' is evaluated every time the function is called.There's no fundamental reason it couldn't have worked that way in Python. (I understand that changing the language so it worked that way now would likely break some code.)
EDIT: fixed formatting.
The equivalent Python would be something like this:
def function():
x = 3
def internal(x, foo=[]):
foo.append([7])
foo.append(x)
return foo
return internal(x)
print function()
print function()
Which does what you would expect: [[7], 3]
[[7], 3]You've still got that &optional argument though. I don't see a huge amount of difference from a semantic point of view between that and the Python version though (ie. if x == None: ...).
fn = lambda y=[y]: y.push(7); return y
if you accept the ; to separate statements, as the lambda in python is syntactically only allowed to contain one statement.(The introduction of the variable x into the example is not important for the behavior of default arguments, however, it is important for a separate issue. I've stripped it out here.)
http://stackoverflow.com/questions/1651154/why-are-default-a...
The code that evaluates the default expression doesn't need to be in a separate function, either, so the argument that calling that function is too expensive also doesn't hold water.
I just tried a test in SBCL:
(defun foo1 (x) x)
(defun test1 (n) (dotimes (i n) (foo1 (cons nil nil))))
(time (test1 100000000))
=> 4.4 sec, or 44ns / iteration
(defun foo2 (&optional (x (cons nil nil))) x)
(defun test2 (n) (dotimes (i n) (foo2)))
(time (test2 100000000))
=> 4.1 sec, or 41ns / iteration
The version with the optional parameter is actually slightly faster, which completely blows a hole in the performance argument.Look, no language is perfect -- not even Common Lisp :-) I think users are better served when design flaws in a language are acknowledged without defensiveness than when bogus justifications are offered.
I don't agree that this is a design flaw. As I recall it bit me once as a beginner, and never again in over a decade of using python, and as a lisp hacker you know you don't design a language for beginners. :-)
Not to say I think python should be changed on this point. It shouldn't, there are code checkers that warn you on the gotcha, let's use them.
def f(x=0, y="foo", z=3.14159):
This, however, is a perfectly Pythonic idiom: def f(L=None):
if L is None:
L = []
[1]: http://docs.python.org/reference/compound_stmts.html#functio...Basically, sometimes you do want to reuse the mutable between function calls, and in those cases it can save a fair bit of code passing it in repeatedly.
def calculate(a, b, c, memo={}):
try:
value = memo[a, b, c] # return already calculated value
except KeyError:
value = heavy_calculation(a, b, c)
memo[a, b, c] = value # update the memo dictionary
return valueYou can use a mutable default argument as an ersatz static variable, e.g. for memoization.
Maybe I misunderstood you, but they are perfectly mutable:
class A
attr_accessor :a
def initialize
@a = []
end
def b x = a
x << 1
end
end
obj = A.new
# => #<A:0x007fd92b2e9850 @a=[]>
obj.b
# => [1]
obj.b
# => [1, 1]
obj.a
# => [1, 1]
If you mean "inline default arguments are not mutable", that's not true either. What is true is that the default argument is evaluated when the function is called, not when it is defined: a = 0
# => 0
x = lambda {|y = (a + 1)| y }
# => #<Proc:0x007fd92b1dae50@(irb):33 (lambda)>
x[]
# => 1
a = 5
# => 5
x[]
# => 6 ruby> class A; end
=> nil
ruby> def foo(bar = A.new); return bar; end
=> nil
ruby> foo
=> #<A:0x00000101985060>
ruby> foo
=> #<A:0x0000010197a6b0>
and get back the same object each time `foo` is called in Ruby.I've even seen a major Python library with this bug (I'm sorry, I don't recall which off-hand). It's really surprising behavior for new Python devs.
>>> def f(x):
... return x + 5
>>> type(f)
<type 'function'>
>>> dis.dis(f.func_code)
2 0 LOAD_FAST 0 (x)
3 LOAD_CONST 1 (5)
6 BINARY_ADD
7 RETURN_VALUE
>>> g = f
>>> g(10)
15
>>> g is f
True
They're just variables in the current scope. If you're quite clever, your brain is already figuring out that has some interesting implications that some libraries use: >>> import socket
>>> socket.gethostbyname('www.google.com')
'74.125.71.103'
>>> socket.gethostbyname = lambda i: '10.0.0.1'
>>> socket.gethostbyname('www.google.com')
'10.0.0.1'
I will not pass judgement on monkey patching like this, just pointing out it's doable. I know for a fact Ruby can as well.Functions just being variables has useful properties when you're doing something like fancy switch/case type things (the readability of this is questionable, but it's cool to look at it, like a Duff's device):
>>> i = 1
>>> { str: func1, unicode: func2, int: func3 }.get(type(i), func1)(i)
in func3
Also consider something like this, which is how decorators work (and they're incredibly useful), sort of like a closure: >>> def maker(i):
... def ret(x):
... return x + i
... return ret
>>> f, g = maker(10), maker(100)
>>> f(5), g(10)
(15, 110)
I'd be surprised if Ruby couldn't do everything I just did.By useful he means mucking up your program in totally unexpected ways.
He's being nice about it being a silly decision to have it behave that way. The entire post reads more like a list of unexpected things that will bite you in the ass.
def f(L=()):
...def f(L=None): L = L or []
These are the little assumptions that keep blowing off my feet. Thanks.
def f(x=None): x if x is not None else []But if this expression is a line in a larger function, and is intended to reset the value of argument x if no other value is passed in for it, does this really act as an assigment to x? Because I sort of read this expression as evaluating to some value -- the passed value for x, or a [] -- but does this assign that value to the argument x? Or must it be x = [expression]
def f(x=None):
x = x if x is not None else []
return x
Now it will assign that value back to x. Otherwise it would just evaluate the expression. def f(x=None): if x is None: x = []That said, people still seem to favor your form as the more Pythonic way. Personally, I think that's just because the ternary expression is relatively new.
L = [] if L is None else L