A: 

data.pop(k,d) deletes the key k from the dict and returns the value that it was associated to (that is data[k]). If k didn't exist in the dict, it returns d.

If you only pass a single argument, if k doesn't exist in the dict, it will raise a KeyError exception instead.

However, I don't understand what that code with *args is trying to do... I don't think it's correct.

Nicolás
+2  A: 
def func(*args): 
    pass

When you define a function this way, *args will be array of arguments passed to the function. This allows your function to work without knowing ahead of time how many arguments are going to be passed to it.

You do this with keyword arguments too, using **kwargs:

def func2(**kwargs): 
    pass

See: Arbitrary argument lists


In your case, you've defined a class which is acting like a dictionary. The dict.pop method is defined as pop(key[, default]).

Your method doesn't use the default parameter. But, by defining your method with *args and passing *args to dict.pop(), you are allowing the caller to use the default parameter.

In other words, you should be able to use your class's pop method like dict.pop:

my_a = a()
value1 = my_a.pop('key1')       # throw an exception if key1 isn't in the dict
value2 = my_a.pop('key2', None) # return None if key2 isn't in the dict
Seth
+2  A: 

The pop method of dicts (like self.data, i.e. {'a':'aaa','b':'bbb','c':'ccc'}, here) takes two arguments -- see the docs (why do you keep being astonished by clearly documented, simple behavior?!).

The second argument, default, is what pop returns if the first argument, key, is absent. (If you call pop with just one argument, key, it raises an exception if that key's absent).

In your example, print b.pop('a',{'b':'bbb'}), this is irrelevant because 'a' is a key in b.data. But if you repeat that line...:

b=a()
print b.pop('a',{'b':'bbb'})
print b.pop('a',{'b':'bbb'})
print b.data

you'll see it makes a difference: the first pop removes the 'a' key, so in the second pop the default argument is actually returned (since 'a' is now absent from b.data).

Alex Martelli
+2  A: 

So many questions here. I see at least two, maybe three:

  • What does pop(a,b) do?/Why are there a second argument?
  • What is *args being used for?

The first question is trivially answered in the Python Standard Library reference:

pop(key[, default])

If key is in the dictionary, remove it and return its value, else return default. If default is not given and key is not in the dictionary, a KeyError is raised.


The second question is covered in the Python Language Reference:

If the form “identifier” is present, it is initialized to a tuple receiving any excess positional parameters, defaulting to the empty tuple. If the form “*identifier” is present, it is initialized to a new dictionary receiving any excess keyword arguments, defaulting to a new empty dictionary.

In other words, the pop function takes at least two arguments. The first two get assigned the names self and key; and the rest are stuffed into a tuple called args.

What's happening on the next line when *args is passed along in the call to self.data.pop is the inverse of this - the tuple *args is expanded to of positional parameters which get passed along. This is explained in the Python Language Reference:

If the syntax *expression appears in the function call, expression must evaluate to a sequence. Elements from this sequence are treated as if they were additional positional arguments

In short, a.pop() wants to be flexible and accept any number of positional parameters, so that it can pass this unknown number of positional parameters on to self.data.pop().

This gives you flexibility; data happens to be a dict right now, and so self.data.pop() takes either one or two parameters; but if you changed data to be a type which took 19 parameters for a call to self.data.pop() you wouldn't have to change class a at all. You'd still have to change any code that called a.pop() to pass the required 19 parameters though.

James Polley
+1 for the effort. I fear that the OP's English is not up to the task of reading your answer.
Adam Bernier
I wrote that before I read the OPs profile. I've now read said profile and seen several other of the OPs questions. I've just made another attempt, this time using only code :)
James Polley
+2  A: 
>>> def func(a, *args, **kwargs):
...   print 'a %s, args %s, kwargs %s' % (a, args, kwargs)
... 
>>> func('one', 'two', 'three', four='four', five='five')
a one, args ('two', 'three'), kwargs {'four': 'four', 'five': 'five'}

>>> def anotherfunct(beta, *args):
...   print 'beta %s, args %s' % (beta, args)
... 
>>> def func(a, *args, **kwargs):
...   anotherfunct(a, *args)
... 
>>> func('one', 'two', 'three', four='four', five='five')
beta one, args ('two', 'three')
>>> 
James Polley
did you even read the question?
SilentGhost
I read several questions, as detailed in my other answer. I also read the OPs profile and realised that my other answer is useless. This answer should demonstrate at least part of what *args does, which is what I'm guessing the OPs real confusion is, despite the subject.
James Polley
@James, +1 for the extra effort. I'd reached the same conclusion about the real question.
Peter Hansen
Nope, apparently I was wrong.
James Polley