class a(object):
data={'a':'aaa','b':'bbb','c':'ccc'}
def pop(self, key, *args):
return self.data.pop(key, *args)#what is this mean.
b=a()
print b.pop('a',{'b':'bbb'})
print b.data
self.data.pop(key, *args) ←------ why is there a second argument?
The pop method of dicts (like self.data, i.e. {'a':'aaa','b':'bbb','c':'ccc'}, here) takes two arguments -- see the docs
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).
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.
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
>>> 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')
>>>
Related
I try to write a small class and want to sort the items based on the weight. The code is provided,
class Bird:
def __init__(self, weight):
# __weight for the private variable
self.__weight = weight
def weight(self):
return self.__weight
def __repr__(self):
return "Bird, weight = " + str(self.__weight)
if __name__ == '__main__':
# Create a list of Bird objects.
birds = []
birds.append(Bird(10))
birds.append(Bird(5))
birds.append(Bird(200))
# Sort the birds by their weights.
birds.sort(lambda b: b.weight())
# Display sorted birds.
for b in birds:
print(b)
When I run the program, I get the error stack of Python TypeError: sort() takes no positional arguments. Whats the issue here?
Exactly what it says: sort doesn't take any positional arguments. It takes a keyword-only argument named key:
birds.sort(key=lambda b: b.weight())
From the documentation:
sort(*, key=None, reverse=False)
This method sorts the list in place,
using only < comparisons between items. Exceptions are not suppressed
- if any comparison operations fail, the entire sort operation will fail (and the list will likely be left in a partially modified state).
sort() accepts two arguments that can only be passed by keyword
(keyword-only arguments):
key specifies a function of one argument that is used to extract a comparison key from each list element (for example, key=str.lower). The key corresponding to each item in the list is calculated once and then used for the entire sorting process. The default value of None means that list items are sorted directly without calculating a separate key value.
[...]
The * in the signature is the separator between positional parameters and keyword-only parameters; its position as the initial "argument" indicates the lack of positional parameters.
Looking at the documentation for list.sort, we can see that key is a keyword-only argument. So change the line
birds.sort(lambda b: b.weight())
to
birds.sort(key=(lambda b: b.weight()))
sort() takes a key argument and nothing else (well, it can take a reverse argument). You supplied sort() with an argument it cannot accept. Just add a key= before your lambda
The error message is because key takes keyword arguments, not positional arguments. A positional argument is a name that is not followed by an equal sign and default value.
In python we can do this:
def myFun1(one = '1', two = '2'):
...
Then we can call the function and pass the arguments by their name:
myFun1(two = 'two', one = 'one')
Also, we can do this:
def myFun2(**kwargs):
print kwargs.get('one', 'nothing here')
myFun2(one='one')
So I was wondering if it is possible to combine both methods like:
def myFun3(name, lname, **other_info):
...
myFun3(lname='Someone', name='myName', city='cityName', otherInfo='blah')
In general what combinations can we do?
Thanks and sorry for my silly question.
The general idea is:
def func(arg1, arg2, ..., kwarg1=default, kwarg2=default, ..., *args, **kwargs):
...
You can use as many of those as you want. The * and ** will 'soak up' any remaining values not otherwise accounted for.
Positional arguments (provided without defaults) can't be given by keyword, and non-default arguments can't follow default arguments.
Note Python 3 also adds the ability to specify keyword-only arguments by having them after *:
def func(arg1, arg2, *args, kwonlyarg=default):
...
You can also use * alone (def func(a1, a2, *, kw=d):) which means that no arguments are captured, but anything after is keyword-only.
So, if you are in 3.x, you could produce the behaviour you want with:
def myFun3(*, name, lname, **other_info):
...
Which would allow calling with name and lname as keyword-only.
Note this is an unusual interface, which may be annoying to the user - I would only use it in very specific use cases.
In 2.x, you would need to manually make this by parsing **kwargs.
You can add your named arguments along with kwargs. If the keys are available in the calling function It will taken to your named argument otherwise it will be taken by the kwargs dictionary.
def add(a=1, b=2,**c):
res = a+b
for items in c:
res = res + c[items]
print(res)
add(2,3)
5
add(b=4, a =3)
7
add(a =1,b=2,c=3,d=4)
10
It's possible at least for Python 2.7. Keyword arguments get assigned to positional parameters by name, so you can do
In [34]: def func(name, lname, **kwargs):
print 'name='+name, 'lname='+lname
print kwargs
....:
In [35]: func(lname='lname_val', name='name_val', city='cityName', otherInfo='blah')
name=name_val lname=lname_val
{'city': 'cityName', 'otherInfo': 'blah'}
Official docs state it that way:
"If keyword arguments are present, they are first converted to positional arguments, as follows. First, a list of unfilled slots is created for the formal parameters. If there are N positional arguments, they are placed in the first N slots. Next, for each keyword argument, the identifier is used to determine the corresponding slot (if the identifier is the same as the first formal parameter name, the first slot is used, and so on). If the slot is already filled, a TypeError exception is raised. Otherwise, the value of the argument is placed in the slot, filling it (even if the expression is None, it fills the slot)."
https://docs.python.org/2/reference/expressions.html#calls
I'm attempting to do a function call with the values of a dictionary.
The function takes a number of arguments, most with default values.
def foo(name, a=None, b='', c=12):
print(name,a,b,12)
If the dictionary is fully populated the function call would look like this.
def call_foo(arg_dict):
foo(name=arg_dict['name'], a=arg_dict['a'], b=arg_dict['b'], c=arg_dict['c'])
I need to make the function call dependent on whether or not those keys actually exist in the dictionary though. So that if only a subset of the arguments exist, I'm only passing those arguments.
def call_foo(arg_dict):
if 'a' in arg_dict and 'b' in arg_dict and 'c' in arg_dict:
foo(name=arg_dict['name'], a=arg_dict['a'], b=arg_dict['b'], c=arg_dict['c'])
elif 'a' in arg_dict and 'c' in arg_dict:
foo(name=arg_dict['name'], a=arg_dict['a'], c=arg_dict['c'])
This type of expression will quickly become unmanageable with a larger number of optional arguments.
How can I define a named argument list to pass to foo? Something similar to the following.
def call_foo(arg_dict):
arg_list = []
arg_list.append(name=arg_dict['name'])
if 'a' in arg_dict:
arg_list.append(a=arg_dict['a'])
if 'b' in arg_dict:
arg_list.append(b=arg_dict['b'])
if 'c' in arg_dict:
arg_list.append(c=arg_dict['c'])
foo(arg_list)
You could call the function using the double-star for kwargs:
def call_foo(arg_dict):
foo(**arg_dict)
This means you probably don't even need call_foo to begin with.
This StackOverflow post has a good amount of detail if you want to know more about how the star and double-star arguments work.
You can simply call
def call_foo(arg_dict):
foo(**arg_dict)
You created a arg_list (no need of it). But if any chance, you can pass list as arguments,
def call_foo(arg_dict):
foo(*arg_list)
It takes arguments to corresponding index of list.
Also no need of if 'a' in dict and 'b' in dict and 'c' in dict:, Just
args = ['a','b','c']
if all([(i in arg_dict) for i in args]):
Dont use dict as variable name or arguments, it may override builtin dict
I am wondering if it is possible to list the variables expected by a Python function, prior to calling it, in order to pass the expected variables from a bigger dict containing a lot of variables.
I have searched the net but couldn't find anything. However, the python interpreter can show the list of expected variables, so there surely must be some way to do it in a script?
You can use either the inspect.signature() or inspect.getfullargspec() functions:
import inspect
argspec = inspect.getfullargspec(somefunction)
signature = inspect.signature(somefunction)
inspect.fullargspec returns a named tuple with 7 elements:
A list with the argument names
The name of the catchall *args parameter, if defined (None otherwise)
The name of the catchall **kwargs parameter, if defined (None otherwise)
A tuple with default values for the keyword arguments; they go with the last elements of the arguments; match these by length of the default values tuple.
A list of keyword-only parameter names
A dictionary of default values for the keyword-only parameter names, if any
and a dictionary containing the annotations
With inspect.signature() you get a Signature object, a rich object that models not only the above data as a more structured set of objects but also lets you bind values to parameters the same way a call to the function would.
Which one is better will depend on your use cases.
Demo:
>>> import inspect
>>> def foo(bar, baz, spam='eggs', *monty, python: "kwonly", spanish=42, **inquisition) -> "return annotation":
... pass
...
>>> inspect.getfullargspec(foo)
FullArgSpec(args=['bar', 'baz', 'spam'], varargs='monty', varkw='inquisition', defaults=('eggs',), kwonlyargs=['python', 'spanish'], kwonlydefaults={'spanish': 42}, annotations={'return': 'return annotation', 'python': 'kwonly'})
>>> signature = inspect.signature(foo)
>>> signature
<Signature (bar, baz, spam='eggs', *monty, python: 'kwonly', spanish=42, **inquisition) -> 'return annotation'>
>>> signature.parameters['python'].kind.description
'keyword-only'
>>> signature.bind('Eric', 'Idle', 'John', python='Cleese')
<BoundArguments (bar='Eric', baz='Idle', spam='John', python='Cleese')>
If you have a dictionary named values of possible parameter values, I'd use inspect.signature() and use the Signature.parameters mapping to match names:
posargs = [
values[param.name]
for param in signature.parameters.values()
if param.kind is Parameter.POSITIONAL_ONLY
]
skip_kinds = {Parameter.POSITIONAL_ONLY, Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD}
kwargs = {
param.name: values[param.name]
for param in signature.parameters.values()
if param.name in values and param.kind not in skip_kinds
}
The above gives you a list of values for the positional-only parameters, and a dictionary for the rest (excepting any *args or **kwargs parameters).
Just as a side answer, I now use another approach to pass to functions the variables they expect: I pass them all.
What I mean is that I maintain a kind of global/shared dictionnary of variables in my root object (which is the parent of all other objects), eg:
shareddict = {'A': 0, 'B':'somestring'}
Then I simply pass this dict to any method of any other object that is to be called, just like this:
shareddict.update(call_to_func(**shareddict))
As you can see, we unpack all the keys/values in shareddict as keyword arguments to call_to_func(). We also update shareddict with the returned result, we'll see below why.
Now with this technic, I can simply and clearly define in my functions/methods if I need one or several variables from this dict:
my_method1(A=None, *args, **kwargs):
''' This method only computes on A '''
new_A = Do_some_stuff(A)
return {'A': new_A} # Return the new A in a dictionary to update the shared value of A in the shareddict
my_method2(B=None, *args, **kwargs):
''' This method only computes on B '''
new_B = Do_some_stuff(B)
return {'B': new_B} # Return the new B in a dictionary to update the shareddict
my_method3(A=None, B=None, *args, **kwargs):
''' This method swaps A and B, and then create a new variable C '''
return {'A': B, 'B': A, 'C': 'a_new_variable'} # Here we will update both A and B and create the new variable C
As you can notice, all the methods above return a dict of variables, which will update the shareddict, and which will get passed along to other functions.
This technic has several advantages:
Quite simple to implement
Elegant way to maintain a shared list of variables but without using a global variable
Functions and methods clearly show in their definitions what they expect (but of course one caveat is that even mandatory variables will need to be set as a keyword argument with a default value such as None, which usually means that the variable is optional, but here it's not
The methods are inheritable and overloadable
Low memory footprint since the same shareddict is passed all along
The children functions/methods define what they need (bottom-up), instead of the root defining what arguments will be passed to children (top-down)
Very easy to create/update variables
Optionally, it's VERY easy to dump all those variables in a file, eg by using json.dumps(finaldict, sort_keys=True).
Nice and easy:
import inspect #library to import
def foo(bar, baz, spam='eggs', *monty, **python): pass #example function
argspec = inspect.signature(foo)
print(argspec) #print your output
prints: (bar, baz, spam='eggs', *monty, **python)
It also works for methods inside classes (very useful!):
class Complex: #example Class
def __init__(self, realpart, imagpart): #method inside Class
... self.r = realpart
... self.i = imagpart
argspec = inspect.signature(Complex)
print(argspec)
prints: (realpart, imagpart)
In python we can do this:
def myFun1(one = '1', two = '2'):
...
Then we can call the function and pass the arguments by their name:
myFun1(two = 'two', one = 'one')
Also, we can do this:
def myFun2(**kwargs):
print kwargs.get('one', 'nothing here')
myFun2(one='one')
So I was wondering if it is possible to combine both methods like:
def myFun3(name, lname, **other_info):
...
myFun3(lname='Someone', name='myName', city='cityName', otherInfo='blah')
In general what combinations can we do?
Thanks and sorry for my silly question.
The general idea is:
def func(arg1, arg2, ..., kwarg1=default, kwarg2=default, ..., *args, **kwargs):
...
You can use as many of those as you want. The * and ** will 'soak up' any remaining values not otherwise accounted for.
Positional arguments (provided without defaults) can't be given by keyword, and non-default arguments can't follow default arguments.
Note Python 3 also adds the ability to specify keyword-only arguments by having them after *:
def func(arg1, arg2, *args, kwonlyarg=default):
...
You can also use * alone (def func(a1, a2, *, kw=d):) which means that no arguments are captured, but anything after is keyword-only.
So, if you are in 3.x, you could produce the behaviour you want with:
def myFun3(*, name, lname, **other_info):
...
Which would allow calling with name and lname as keyword-only.
Note this is an unusual interface, which may be annoying to the user - I would only use it in very specific use cases.
In 2.x, you would need to manually make this by parsing **kwargs.
You can add your named arguments along with kwargs. If the keys are available in the calling function It will taken to your named argument otherwise it will be taken by the kwargs dictionary.
def add(a=1, b=2,**c):
res = a+b
for items in c:
res = res + c[items]
print(res)
add(2,3)
5
add(b=4, a =3)
7
add(a =1,b=2,c=3,d=4)
10
It's possible at least for Python 2.7. Keyword arguments get assigned to positional parameters by name, so you can do
In [34]: def func(name, lname, **kwargs):
print 'name='+name, 'lname='+lname
print kwargs
....:
In [35]: func(lname='lname_val', name='name_val', city='cityName', otherInfo='blah')
name=name_val lname=lname_val
{'city': 'cityName', 'otherInfo': 'blah'}
Official docs state it that way:
"If keyword arguments are present, they are first converted to positional arguments, as follows. First, a list of unfilled slots is created for the formal parameters. If there are N positional arguments, they are placed in the first N slots. Next, for each keyword argument, the identifier is used to determine the corresponding slot (if the identifier is the same as the first formal parameter name, the first slot is used, and so on). If the slot is already filled, a TypeError exception is raised. Otherwise, the value of the argument is placed in the slot, filling it (even if the expression is None, it fills the slot)."
https://docs.python.org/2/reference/expressions.html#calls