0

This below snippet of code gives me this error TypeError: pop() argument after ** must be a mapping, not tuple.

class a():
    data={'a':'aaa','b':'bbb','c':'ccc'}
    def pop(self, key, **args):
        return self.data.pop(key, **args)

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

But when I replace double ** with single *, this works fine. As per my understanding , if we are passing a dictionary , we should have double **. In this case the second argument what's being passed is dictionary {'b':'bbb'}. Then how is it throwing error in first case but not in second case?

class a():
    data={'a':'aaa','b':'bbb','c':'ccc'}
    def pop(self, key, *args):
        return self.data.pop(key, *args)

b=a()
print(b.pop('a',{'b':'bbb'})
martineau
  • 119,623
  • 25
  • 170
  • 301
Kumar
  • 949
  • 1
  • 13
  • 23
  • 1
    With your first example, I get `TypeError: pop() takes 2 positional arguments but 3 were given`. This is because `{'b':'bbb'}` is passed as a _positional_ argument, not a keyword argument. In `**args`, the `**` indicates that you are taking keyword arguments. These keyword arguments are available in the function as a dictionary. E.g., `pop('a', b='bbb')` would give you `args = {'b': 'bbb'}` inside `pop()`. It has nothing to do with passing dictionaries. – Pranav Hosangadi Jul 27 '21 at 20:06
  • 1
    `dict.pop` doesn't take keyword arguments. – Barmar Jul 27 '21 at 20:07
  • When you use `**args` in the `a.pop()` definition, it means that the function takes keyword arguments after `key`. But you aren't calling it with any keywords. And what are you specting `**args` to mean when you pass it to `self.data.pop()`? – Barmar Jul 27 '21 at 20:11
  • 1
    "As per my understanding , if we are passing a dictionary , we should have double `**`" Your understanding is wrong. Double-asterisks indicate *variadic keyword arguments*, i.e., any number of keyword arguments. so if you do `def foo(**args): ..` you can call `foo` in the following ways: `foo(); foo(a=1); foo(banana=[], bar=2)` and so on. These arguments will be provided **as a dictionary**. Now, you can *unpack* mappings (e.g. a dict) using `**a_mapping`, as an argument to a function. They will be *passed as keyword arguments*, and handled in different ways depending on the exact signature – juanpa.arrivillaga Jul 27 '21 at 20:29
  • @juanpa.arrivillaga thanks – Kumar Jul 27 '21 at 20:39
  • @juanpa.arrivillaga Then should not this work ? ``` class a(): data={'a':'aaa','b':'bbb','c':'ccc'} def pop(self, key, *args): return self.data.pop(key, *args) b=a() print(b.pop('a',1,2,3)) ``` – Kumar Jul 27 '21 at 20:41
  • sorry for unable to edit the code piece – Kumar Jul 27 '21 at 20:42
  • 1
    @Kumar no, it shouldn't. Have you *read the signature for `dict.pop`*? `dict.pop` accepts a key to pop, and a default value to return if the key doesn't exist. So at most, two positional arguments. When you do `b.pop('a', 1, 2, 3)` you are passing *4* positional arguments. So it will raise a TypeError. You seem to not understand the `dict.pop` method, it's hard to say what you are *expecting*. – juanpa.arrivillaga Jul 27 '21 at 20:56

1 Answers1

2

If you want a dictionary to be used as keyword arguments, you have to use the ** in the call as well:

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

But I don't think that's really what you wanted anyway.

Tim Roberts
  • 48,973
  • 4
  • 21
  • 30