1

Possible Duplicate:
“Least Astonishment” in Python: The Mutable Default Argument

I'm using the MailSnake in Python, which is a wrapper for the MailChimp API.

Now I'm getting some curious behaviour for a function I've written to pull lists of subscribers we have. This is the code I'm using:

from mailsnake import MailSnake
from mailsnake.exceptions import *

ms = MailSnake('key here')

def return_members (status, list_id, members = [], start = 0, limit = 15000, done = 0):
    temp_list = ms.listMembers(status=status, id=list_id, start=page, limit=limit, since='2000-01-01 01:01:01')
    for item in temp_list['data']:  # Add latest pulled data to our list
        members.append(item)
    done = limit + done
    if done < temp_list['total']:  # Continue if we have yet to 
        start = start + 1
        if limit > (temp_list['total'] - done):  # Restrict how many more results we get out if are on the penultimate page
            limit = temp_list['total'] - done
        print 'Making another API call to get complete list'
        return_members(status, list_id, members, page, limit, done)
    return members

for id in lists:
    unsubs = return_members('subscribed',id)
    for person in unsubs:
        print person['email']

print 'Finished getting information'

So this function runs recursively until we have pulled all members from a given list.

But what I've noticed is that the variable unsubs seems to just get bigger and bigger. In that when the function return_members is called with different list ids, I get an amalgamation of the emails of every list I have called so far (rather than just one particular list).

If I call return_members('subscribed', id, []) which explicitly gives it a fresh array then it's fine. But I don't see why I need to do this, as if I am calling the function with a different list ID, it's not running recursively and since I haven't specificed the members variable, it defaults to []

I think this may be a quirk of python, or I've just missed something!

Community
  • 1
  • 1
penguin
  • 856
  • 3
  • 14
  • 30

2 Answers2

2

The linked SO infamous question by Martjin would help you understand the underline issue, but to get this sorted out you can write the following loop

for item in temp_list['data']:  # Add latest pulled data to our list
    members.append(item)

to a more pythonic version

members = members + temp_list['data'] # Add latest pulled data to our list

this small change would ensure that you are working with an instance different from the one passed as the parameter to return_members

Abhijit
  • 62,056
  • 18
  • 131
  • 204
  • 2
    Or just `members.extend(temp_list['data'])`. – Blender Dec 29 '12 at 18:43
  • @Blender: But, wouldn't `members` still refer the same variable passed as the parameter, which would mean the problem remains unresolved. From the [docs](http://docs.python.org/2/tutorial/datastructures.html) ` list.extend(L) Extend the list by appending all the items in the given list; equivalent to a[len(a):] = L` – Abhijit Dec 29 '12 at 18:47
  • 1
    Your original code didn't address that problem either. – Blender Dec 29 '12 at 18:49
  • @Blender: "Your original code didn't address that problem either", can you please amplify – Abhijit Dec 29 '12 at 18:51
  • Huh, apparently `a = a + [1]` is different from `a += [1]`. Can you explain why your change works? – Blender Dec 29 '12 at 18:56
  • @Blender: `a += [1]` is an inplace add where as `a = a + [1]` creates a new instance of `a` – Abhijit Dec 29 '12 at 18:57
1

Try replacing:

def return_members (status, list_id, members = [], start = 0, limit = 15000, done = 0):

with:

def return_members (status, list_id, members = None, start = 0, limit = 15000, done = 0):
    if not members: members = []
Hyperboreus
  • 31,997
  • 9
  • 47
  • 87