Passing keyword arguments with a hyphen - python

I have a keyword argument function:
def f1(**kw):
for key,val in kw.iteritems():
print "key=%s val=%s" % (key,val)
f1(Attr1 = "Val1", Attr2 = "Val2") # works fine.
f1(Attr1-SubAttr = "Val1", Attr2 = "Val2") # complains about keyword being an expression.
f1("Attr1-SubAttr" = "Val1", Attr2 = "Val2") # doesn't work either.
How do I pass in keywords with a hyphen? I don't have control over these keywords since I am parsing these from an existing legacy database.

Keyword arguments must be valid Python identifiers; these don't allow for - as that's reserved for subtraction.
You can pass in arbitrary strings using the **kwargs variable keyword argument syntax instead:
f1(**{"Attr1-SubAttr": "Val1", "Attr2": "Val2"})

To make the above code to work in python 3 iteritems() replaced by items().

Related

How to make a function around dict.update() that similarly can take both positional and key-word args?

The built-in update function to dictionaries can take either, tuples or dictionaries as arguments. I want to make a function around it that also can take both tuples and dictionaries.
parDict = {}
parDict['a'] = 1
parDict['b'] = 2
parDict['group1.a'] = 3
I can do both:
parDict.update(a=2, b=3)
parDict.update({'group1.a':4})
Define a function
def par(**x):
parDict.update(x)
I can do
par(a=2, b=3)
but I cannot do
par({'group1.a' : 4})
The error message is: par() takes 0 positional arguments but 1 was given
In my eyes {'group1.a' : 4} is a key-word argument (although 'group1.a' is not an identifier but a string. The same error text if I address the other parameter {'a': 4} which has an identifier.
If I in the function declaration change par(**x) to par(x) then I can do
par({'group1.a' : 4})
but not
par(a=2, b=3)
I can of course make an alias
par = parDict.update
This works for both types of arguments, but I want to do some more and really need a function here (to make some checks before update).
How should I improve the argument declaration, or the code, of par() to handle both types of arguments?
If i understand well, this would work for you:
def par(*args, **kwargs):
## Merge positional and keyword arguments
kwargs.update(*args)
## Do checks and stuff on kwargs before update
# ....
parDict.update(kwargs)

In Python, how to add a keyword argument to a function with *args and **kwargs? [duplicate]

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

What does replace = options.pop('replace', True) accomplish in this code? [duplicate]

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')
>>>

Python - Pass argument if defined, else use default

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

SyntaxError with dict()

When I try to create this
self.cmds = {
'help' : self.cmdsHelp,
'write-start' : self.startWriter,
'write' : self.writeTo,
'read-start' : self.startReader,
'read' : self.readFrom
}
with the built-in dict() function... i.e.
self.cmds = dict(
help = self.cmdsHelp,
write-start = self.startWriter,
write = self.writeTo,
read-start = self.startReader,
read = self.readFrom
)
... I get this error:
write-start = self.startWriter,
^
SyntaxError: keyword can't be an expression
The dictionary with the curly brackets ({}) -- whatever special name that is -- works, but I cannot fathom why the "newer version" (the dict() form) does not work. Is there something that I am missing, or do you just have to use the curly braces?
For clarity:
Each value in the dictionary is a function (and yes I did remove self., and I also tried to do both self.function() and function() so that when I called it I didn't have to do self.cmds[<input>]() but could rather do self.cmds[<input>])
Keyword arguments must be valid python identifiers. You cannot use - in valid identifiers (you are trying to subtract two identifiers instead). dict() is just a callable, and keyword arguments passed to it are no exception.
Use the {} literal dict syntax instead:
self.cmds = {
'help': self.cmdsHelp,
'write-start': self.startWriter,
'write': self.writeTo,
'read-start': self.startReader,
'read': self.readFrom,
}
because then you can use any valid immutable & hashable value as keys.
Alternatively, use valid identifiers; replace - with _, a character that is allowed in indentifiers:
self.cmds = dict(
help=self.cmdsHelp,
write_start=self.startWriter,
write=self.writeTo,
read_start=self.startReader,
read=self.readFrom,
)
Any other alternatives get ugly real fast; you could use the dict literal syntax to produce a **kwargs double-splat keyword argument mapping:
self.cmds = dict(
help=self.cmdsHelp,
write=self.writeTo,
read=self.readFrom,
**{
'read-start': self.startReader,
'write-start': self.startWriter,
}
)
but that's not any more readable, is it.
You can set those peskey non-identifier keys after the fact:
self.cmds = dict(
help=self.cmdsHelp,
write=self.writeTo,
read=self.readFrom,
)
self.cmds['read-start'] = self.startReader
self.cmds['write-start'] = self.startWriter
but that's more ugly still.
Note that dictionary displays (the official term for the syntax) are faster for the interpreter to process than are dict() calls, as fewer bytecode instructions are used to build one and no function call is involved.

Categories

Resources