News for hackers. Stuff that matters.
Re:Anti-Patterns in Python Programming by cabalamat on Wednesday July 09, 2014 @08:11AM (#8009196)
> Mutables as default function/method arguments

It would really make sense to change the semantics of Python to fix this issue.

Re:Anti-Patterns in Python Programming by bkeroack on Wednesday July 09, 2014 @08:58AM (#8009361)
Change them how, to no longer have functions be first class objects? The behavior of mutable default arguments is clear if you know how Python treats function objects. Any "fix" would handicap the language.
Re:Anti-Patterns in Python Programming by yoo-interested on Wednesday July 09, 2014 @10:42AM (#8009841)
> to no longer have functions be first class objects?

There are other dynamic languages with functions as first class objects which don't share the "mutable default arguments" gotcha.

But having said that, any change regarding this would break backward compatibility.

Re:Anti-Patterns in Python Programming by jcampbell1 on Wednesday July 09, 2014 @10:26AM (#8009760)
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?
Re:Anti-Patterns in Python Programming by dalke on Wednesday July 09, 2014 @11:08AM (#8010006)
As 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.

Re:Anti-Patterns in Python Programming by syllogism on Wednesday July 09, 2014 @05:08PM (#8012247)
In 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.

Re:Anti-Patterns in Python Programming by dalke on Wednesday July 09, 2014 @10:54PM (#8013337)
I 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?

Re:Anti-Patterns in Python Programming by syllogism on Friday July 11, 2014 @03:44PM (#8022012)
Ah I see. Yeah nonlocal can't fix that.
Re:Anti-Patterns in Python Programming by ollysb on Thursday July 10, 2014 @10:37AM (#8015093)
Why not just memoize the result of the slow function?
Re:Anti-Patterns in Python Programming by dalke on Thursday July 10, 2014 @12:28PM (#8015535)
I 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.

Re:Anti-Patterns in Python Programming by bkeroack on Wednesday July 09, 2014 @10:46AM (#8009862)
How would the above work for duck-typed lists, for example? How would the language "know" which types are mutable?
Re:Anti-Patterns in Python Programming by kyllo on Wednesday July 09, 2014 @10:59AM (#8009950)
No, you could just evaluate the expression every time the function is called (like Ruby does) instead of evaluating it once at the time the function is defined (like Python does).
Re:Anti-Patterns in Python Programming by klibertp on Wednesday July 09, 2014 @08:47AM (#8009329)
Remove a `static` keyword from C (used inside a function) while you're at it...