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'})
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.
Related
My question is regarding the following code:
def foo(*args):
return *args # syntax error
def bar(*args):
return 0, *args # ok
def foobar(*args):
if len(args) == 1:
return args[0]
return args
print(bar(1,2))
print(foobar(1,2))
print(foobar(1))
>>> (0,1,2)
>>> (1,2)
>>> 1
is there why reason why foo, rather than being invalid Python code, does not have the same behaviour as foobar? I guess I would also be willing to accept foo producing a singleton tuple i.e. (1,) = foo(1). Any insight into this would be appreciated!
As mentioned in the comments, usefulness of such construct is rather dubious. Esp. since you end up returning tuple of unpacked values... so why not return the tuple itself?
If you really wanted to make this work, you could, starting with Python 3.5 say:
return (*args,)
In line with PEP-448. This unpacks args items into tuple that is to be returned.
And starting with Python 3.8, you could drop the enclosing parenthesis:
Generalized iterable unpacking in yield and return statements no longer requires enclosing parentheses...
return *args,
Your bar() does essentially the same using that generalized unpacking behavior as described in the linked PEP, just having leading item in the tuple.
With regards to the explanation, I believe the comments made above have addressed it.
I wrote the following code and got it to return (1,). You can also follow the suggestion and simply remove the * in front of args in your original code as the comments suggest.
def foo(*args):
return *args,
def bar(*args):
return 0, *args # ok
def foobar(*args):
if len(args) == 1:
return args[0]
return args
print(bar(1,2))
print(foobar(1,2))
print(foobar(1))
print(foo(1))
Here is a picture of the output. I am using Python 3.8
It returns the singleton tuple, as you desire. Is this what you needed?
I need a Python method to have access to self for instance variables and also be able to take any number of arguments. I basically want a method foo that can be called via
foo(a, b, c)
or
foo()
In the class, I think the constructor would be
def foo(self, *args):
Is this correct? Also, fyi, I am new to Python (if you can't tell).
You just have to add it after the self parameter:
class YourClass:
def foo(self, *args):
print(args)
def bar(self, *args, **kwargs):
print(args)
print(kwargs)
def baz(self, **kwargs):
print(kwargs)
I have also added a method in which you also add **kwargs, and the case in which you add both *args and **kwargs.
Examples
>>> o = YourClass()
>>> o.foo()
()
>>> o.foo(1)
(1,)
>>> o.foo(1, 2)
(1, 2)
def foo(self, *args):
Yes, that is correct.
You declared the method correctly. You can also use double asterisks to accept keyword arguments.
Reference: Expressions
A double asterisk ** denotes dictionary unpacking. Its operand must be a mapping. Each mapping item is added to the new dictionary. Later values replace values already set by earlier key/datum pairs and earlier dictionary unpackings.
....
An asterisk * denotes iterable unpacking. Its operand must be an iterable. The iterable is expanded into a sequence of items, which are included in the new tuple, list, or set, at the site of the unpacking.
Args will be a tuple. To access the values you will have to iterate or use positional arguments, ie: args[0]
I have a dictionary of functions, all of which use 1 or 2 optional arguments. I want to iterate through this dictionary and pass both arguments to each iterated function, and have the functions that only need 1 argument to ignore the second. In these cases, however, I get an unexpected keyword argument error.
def getNumFrames(self, **kwargs):
return len(self.x)
def getConcentration(self, **kwargs):
if ((not gradient) or (not maxX)):
return 0
return (gradient / maxX) * self.x[0]
fields = {'numFrames': getNumFrames, 'concentration': getConcentration}
for field, fieldFunction in fields.items():
for track in tracks:
fieldFunction(object, maxX = 10, gradient = 2)
In this example, getConcentration would work, but getFrames would say maxX is an unexpected keyword.
*I edited my post to include my actual minimalist code, as was suggested.
A much better way to avoid all of this trouble is to use the following paradigm:
def func(obj, **kwargs):
return obj + kwargs.get(a, 0) + kwargs.get(b,0)
This makes use of the fact that kwargs is a dictionary consisting of the passed arguments and their values and get() performs lookup and returns a default value if the key does not exist.
Always access your named arguments through the dictionary kwargs. This makes your life much simpler.
I would go about this by creating a function like the one below with the second parameter optional.
func (obj, a = 0, b = 0):
return obj + a + b
I'm trying to make a function designed to call another function multiple times:
def iterator(iterations, function, *args):
#called as:
iterator(5, my_function, arg1, arg2, arg3)
Note that the number of arguments here is variable: could 1, could be 2, could be 10.
fill them in based on the function that is being called.
def iterator(iterations, function, *args):
for i in range(iteration):
temp = function(args)
return temp
The problem here is:
TypeError: my_function() takes exactly 4 arguments (1 given)
And this is because (arg1, arg2, arg3, arg4) are being treated as a single argument.
How do I get around this?
By using the same syntax when applying the args sequence:
temp = function(*args)
The *args syntax here is closely related to the *args function parameter syntax; instead of capturing an arbitrary number of arguments, using *args in a call expands the sequence to separate arguments.
You may be interested to know that there is a **kwargs syntax too, to capture and apply keyword arguments:
def iterator(iterations, function, *args, **kwargs):
for i in range(iteration):
temp = function(*args, **kwargs)
return temp
Try this, unpacking the argument list (a.k.a. splatting it):
function(*args)
From the example in the documentation, you'll see that this is what you need:
range(3, 6) # ok
range([3, 6]) # won't work
range(*[3, 6]) # it works!
I call a method of an external library multiple times in my class like this:
class MyClass:
const_a = "a"
const_b = True
const_c = 1
def push(self, pushee):
with ExternalLibrary.open(self.const_a, self.const_b, self.const_c) as el:
el.push(pushee)
def pop(self):
with ExternalLibrary.open(self.const_a, self.const_b, self.const_c) as el:
return el.pop()
The lines containing the with statement are bugging me, because they require passing the the constants as arguments every time. I would like to store the arguments in a predefined data structure like a tuple and pass that to the external library.
You can do this:
args = (const_a, const_b, const_c)
ExternalLibrary.open(*args)
The * syntax unpacks an iterable (tuple, list, etc.) into individual arguments in a function call. There is also a ** syntax for unpacking a dictionary into keyword arguments:
kwargs = {'foo': 1, 'bar': 2}
func(**kwargs) # same as func(foo=1, bar=2)
You can also use both in the same call, like func(*args, **kwargs).