Cowabunga! Teenage Mutable Default Arguments in Python

I was writing a Python function yesterday to which I passed a list as an argument.

After manipulating the list I passed, I returned my desired value.

What was a little surprising to me was that the initial list itself got updated.

For example, consider the following:

>>> def return_new_list(old_list):
      old_list.append("new stuff")
      return old_list

>>> my_list = ["a"]
>>> print return_new_list(my_list)
['a', 'new stuff']
>>> print my_list
['a', 'new stuff']

I would have expected that the list "my_list" would still only have the original item, "a".

Instead, "my_list" itself has been altered per the function "return_new_list()".

So, after being flustered I stumbled onto this solution which uses the built-in "list()" function within the "return_new_list()" function.

>>> def return_new_list(old_list):
      old_list = list(old_list) # this seems to "fix" the "problem".
      old_list.append("new stuff")
      return old_list

>>> my_list = ["a"]
>>> print return_new_list(my_list)
['a', 'new stuff']
>>> print my_list
['a']

Of course, after searching for why this behavior is as it is, I found this page (see: "Mutable Default Arguments") which explained why Python does what it does and also presents a different way of dealing with the issue.

But, here too with the author's example code, it seems to work just fine to use the "list()" function within the function "append_to()".

>>> def append_to(element, to=[]):
      to = list(to) # I'm a new line!
      to.append(element)
      return to

>>> print append_to(12)
[12]
>>> print append_to(42)
[42]

Anyway, just thought I'd share.

I'd love to hear any opinions on why the "solution" I proposed works, isn't a good idea, or if ninja kicks the damn rabbit, etc.

--------------

Related Content:

5 Comments

  1. Michael Scott Cuthbert

    Both of the two solutions are great, but there are slight differences depending on the context.  In the case of a list, each one works as well as the other, but for other TMNT arguments, it can make a huge difference.  What I like about the "to=[]" style is that it gives a sense of the type of argument that should be passed to "to" from reading the code.  In a system that uses something like, say, Sphinx, to generate documentation from code, then this type of function signature can be really helpful.

    However, if the default value takes a long time to generate: 

    def transposeScore(score = Score('WagnerRingCycle') ):

    then putting a default value, requires it to be created at compile time and then either not called, or discarded.  Your solution only creates the list if the function is ever called, so it's better for slow-to-create default items. (Again, it hardly matters with a list).

    What if you want to give people the option of calling append_to with a new list if called with no arguments, but appending to the given list if called with an argument?  You can do something like:

    def append_to_new(element, to = list):

    …if callable(to):

    … … to = to()

    What can be great about this is that people can pass in functions, etc., that when called will return a new item that can be appended to.   (btw. I like your blog a lot). 

    Reply
    1. nitin (Post author)

      Hey Michael,

      Thanks for the great feedback. It’s given me some good stuff to think about.

      And thanks also for reading the blog.

      p.s. I’m pretty focused these days on deciding if I want to make a career move and/or relocate, but I haven’t forgotten about music21.

      I’ve been banging around thoughts on how I could do something fun with it. Do you know of anyone using it in conjunction with speech recognition?

      I know there are some apps that convert sung notes to notation, but the thought of a hands-free way of writing melodic and even contrapuntal lines kind of intrigues me.

      Reply
  2. Michael Scott Cuthbert

    It should be possible to do something with music21 for that.  Audio processing is not music21's strong suit — we have a very rudamentary audio -> pitch/rhythm extractor in the music21.audioSearch module, but it's unlikely to do very well with speech. Better would be to hook into a better pitch extraction library (which will likely give offset timing and frequency or midi note) and then send that data to music21 to manipulate.

    Sorry for the delay in responding; somehow I don't have comment notification turned on here so I only saw this when you did another blog post. Good luck with the career (move or not move).

    Reply
    1. nitin (Post author)

      Hey Michael,

      I was thinking of doing something similar: speech recongnition with an external tool > then send to Music21 to convert to MusicXML > then show in browser with score-viewer (https://github.com/navigator117/score-library/) … or something like that. I was wondering about built in speech recognition with Android or Chrome Speech API.

      No worries on delays – now I you an apology on taking so long.

      Reply
    2. nitin (Post author)

      ps: I'm also wondering about using score-viewer for displaying MusicXML scores within ePUB 3.0 files. That's probably a lower hanging fruit in terms of experiments.

      Reply

Leave a Comment

Your email address will not be published. Required fields are marked *


*