I work with library that has function with signature f(*args, **kwargs).
I need to pass python dict in kwargs argument, but dict contains not strings in keywords
f(**{1: 2, 3: 4})
Traceback (most recent call last):
File "<console>", line 1, in <module>
TypeError: f() keywords must be strings
How can I get around this without editing the function?
Non-string keyword arguments are simply not allowed, so there is no general solution to this problem. Your specific example can be fixed by converting the keys of your dict to strings:
>>> kwargs = {1: 2, 3: 4}
>>> f(**{str(k): v for k, v in kwargs.items()})
I think the best you can do is filter out the non-string arguments in your dict:
kwargs_new = {k:v for k,v in d.items() if isinstance(k,str)}
The reason is because keyword arguments must be strings. Otherwise, what would they unpack to on the other side?
Alternatively, you could convert your non-string keys to strings, but you run the risk of overwriting keys:
kwargs_new = {str(k):v for k,v in d.items()}
-- Consider what would happen if you started with:
d = { '1':1, 1:3 }
Related
Mypy considers this to be valid with strict = true:
from typing import Dict, TypeVar
KeyType = TypeVar("KeyType")
ValueType = TypeVar("ValueType")
class InvertibleDict(Dict[KeyType, ValueType]):
def __inverse__(self) -> "InvertibleDict[ValueType, KeyType]":
new_instance: "InvertibleDict[ValueType, KeyType]" = self.__class__()
for key, value in self.items():
new_instance[value] = key
return new_instance
However, it does not accept the following, more concise version of the same code, saying that "Keywords must be strings" on the last line:
from typing import Dict, TypeVar
KeyType = TypeVar("KeyType")
ValueType = TypeVar("ValueType")
class InvertibleDict(Dict[KeyType, ValueType]):
def __inverse__(self) -> "InvertibleDict[ValueType, KeyType]":
return self.__class__(**{value: key for key, value in self.items()})
MyPy is correct here, it is catching a bug in your implementation (the beauty of static type checking). The type of:
{value: key for key, value in self.items()}
Is Dict[KeyType, ValueType], but that will fail in general when you do:
dict(**some_mapping)
Where the keys are not guaranteed to be strings.
Observe:
>>> dict(**{1:2,3:4})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: keywords must be strings
You just want:
return self.__class__({value: key for key, value in self.items()})
Which won't fail in general:
>>> dict({1:2,3:4})
{1: 2, 3: 4}
Personally, I would go with your first implementation regardless to not unnecessarily waste 2x the amount of space required, and do a needless second-pass.
Note, you would probably never use ** unpacking to initialize a dict, the keyword-argument form of the constructor is a convenience for writing something like:
>>> dict(foo=1, bar=2)
{'foo': 1, 'bar': 2}
You can even use this handy trick when copying a dictionary but wanting to force a value for particular string keys:
>>> dict({'foo': 1, 'bar': 2}, bar=42)
{'foo': 1, 'bar': 42}
Just for laughs I tried return self.__class__({value: key for key, value in self.items()}), which seems to work the same and passes mypy checks. TIL dicts can be initialised with a dict rather than **kwargs.
listyList = [1, 5, 7, 9999]
multiple = [(f, f*100) for f in listyList]
multipleDict = {f[0]:f[1] for f in multiple}
multipleDict = {v:k for k, v in multipleDict}
print (multipleDict[700])
I am a beginner and tried to reverse a dictionary similar to an example in a tutorial. As far as I can tell I should be iterating through a dictionary and not an int? yet I receive this error when I try to run it
Traceback (most recent call last):
File "C:\Users\User\Desktop\test.py", line 5, in <module>
multipleDict = {v:k for k, v in multipleDict}
File "C:\Users\User\Desktop\test.py", line 5, in <dictcomp>
multipleDict = {v:k for k, v in multipleDict}
TypeError: 'int' object is not iterable
I think it will help understand the error a little more if we look at a roughly equivalent for loop, first of all using a correct expression:
for item in multipleDict.items():
k, v = item
...
Here, the items method of the dictionary returns an iterator over 2-element tuples of the form (key, value), and in each iteration of the for loop, the variable item contains just one of these 2-element tuples. This can then be unpacked into multiple variables (in this case, 2 variables) in an unpacking assignment -- which requires iterating over the elements of the tuple.
One could also omit the item variable, and write simply:
for k, v in multipleDict.items():
....
and this is closer to what you would use in a dictionary comprehension.
Now for the version that went wrong -- or a for loop equivalent of it. I'll go back to writing it using an item variable, although again, it doesn't require it.
for item in mutipleDict:
k, v = item
...
When you iterate over a dictionary, you iterate over the keys, so this would also be equivalent to:
for item in mutipleDict.keys():
k, v = item
...
In this case, the keys of your dictionary are just integers -- here 1, 5, 7, and 9999 (not necessarily in that order). So on the first iteration, item would contain one of these integers, and it is trying to unpack it into multiple variables. But you can't iterate over an integer, which is why you get the error that you saw.
Going back to the original dictionary comprehension, the corrected version is just:
multipleDict = {v:k for k, v in multipleDict.items()}
You need to iterate over them items() of the dict:
listyList = [1, 5, 7, 9999]
multiple = [(f, f*100) for f in listyList]
multipleDict = {f[0]:f[1] for f in multiple}
multipleDict = {v:k for k, v in multipleDict.items()}
Need to iterate them over in the last step using the items() function of the dictionary:
multipleDict = {v:k for k, v in multipleDict.items()}
Alternatively, that's not even needed if you switch the value in step 3, try the below:
listyList = [1, 5, 7, 9999]
multiple = [(f, f*100) for f in listyList]
multipleDict = {f[1]:f[0] for f in multiple}
print(multipleDict[700])
:)
I want to build a very tiny functional pipeline in my code to reduce a bunch of functions on a single dictionary. Every function does 1 thing, but on different keys, here's a simplified example:
I have this dictionary
d = {"me": "gab", "you": "cita"}
and suppose a silly function like this:
def make_caps(dic, on=None):
if on is not None:
to_cap = dic.get(on, None)
if to_cap is not None:
dic[on] = to_cap.upper()
return dic
I would like to have this working:
def reduce_fns_on_dict(fns, dic):
from functools import reduce
return reduce(lambda d, f: f(d), fns, dic)
new_dic = reduce_fns_on_dict(fns=[make_caps(on="me"), make_caps(on="you")],
dic=d)
print(new_dic)
on console
{'me': 'GAB', 'you': 'CITA'}
But when I run the above I get:
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: make_caps() missing 1 required positional argument: 'dic'
make_caps(on="me") calls the make_caps function. What you want is a new function which behaves like make_caps(some_arg, on='me'). You can make such a function with functools.partial. The line will look something like fns=[partial(make_caps, on="me")....
EDIT: Alternatively, you could curry the function.
To complete the answer from #FiddleStix, I suggest you to use dictionary comprehension, which I find well-suited in your case:
d = {"me": "gab", "you": "cita"}
new_dic = {k:v.upper() for k,v in d.items() if k in ["me","you"]}
print(new_dic)
# {'me': 'GAB', 'you': 'CITA'}
Note: you can remove if k in ["me","you"] if you want to capitalize all the values of the dictionary.
Suppose i have the list of dictionaries like this
dlist = [d1, d2, d3, d4]
Now d1 inturn are the dictionaries of dictionaries like
d1 = {'dd1':{'a':2. 'ddd1':'moredict'}}
and inside can be many more dictionaries
Is there single line function which can convert all those dictionaries into deafult.
I want that if some key don't exist in any of child dict then it don't get key error.
EDIT:
#something like this , but i was looking for something buildin
def convert_dict(dictionary):
for key, value in dictionary.iteritems():
if isinstance(value, dict):
dictionary[key] = defaultdict(list, value)
convert_dict(value)
I want that if some key don't exist in any of child dict then it don't
get key error.
Setting defaults is one option for this, or simply use get:
>>> d = {'key': 1}
>>> d['foo']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'foo'
>>> d.get('foo')
get will return None if a key doesn't exist, instead of raising KeyError. You can have it return a default value other than None, by passing it in:
>>> d.get('foo',{})
{}
As dbaupp already stated, would it not be better to write a recursive function instead? Because your dictionaries can be nested, I don't think there is a 'pretty' way to handle this otherwise.
def dict_to_ddict(d, defaultfunction):
'''Convert (nested) dict to defaultdict'''
ddict = defaultdict(defaultfunction)
for k, v in d.iteritems():
if isinstance(v, dict):
ddict[k] = dict_to_ddict(v, defaultfunction)
else:
ddict[k] = v
return ddict
You can make this more compact, but that does not make it easier to read.
Python seems to have an inconsistency in what kind of keys it will accept for dicts. Or, put another way, it allows certain kinds of keys in one way of defining dicts, but not in others:
>>> d = {1:"one",2:2}
>>> d[1]
'one'
>>> e = dict(1="one",2=2)
File "<stdin>", line 1
SyntaxError: keyword can't be an expression
Is the {...} notation more fundamental, and dict(...) just syntactic sugar? Is it because there is simply no way for Python to parse dict(1="one")?
I'm curious...
This is not a dict issue, but an artifact of Python syntax: keyword arguments must be valid identifiers, and 1 and 2 are not.
When you want to use anything that is not a string following Python identifier rules as a key, use the {} syntax. The constructor keyword argument syntax is just there for convenience in some special cases.
dict is a function call, and function keywords must be identifiers.
As other answer have stated, dict is a function call. It has three syntactic forms.
The form:
dict(**kwargs) -> new dictionary initialized with the name=value pairs
in the keyword argument list. For example: dict(one=1, two=2)
The keys (or name as used in this case) must be valid Python identifiers, and ints are not valid.
The limitation is not only the function dict You can demonstrate it like so:
>>> def f(**kw): pass
...
>>> f(one=1) # this is OK
>>> f(1=one) # this is not
File "<stdin>", line 1
SyntaxError: keyword can't be an expression
However, there are two other syntactic forms of you can use.
There is:
dict(iterable) -> new dictionary initialized as if via:
d = {}
for k, v in iterable:
d[k] = v
Example:
>>> dict([(1,'one'),(2,2)])
{1: 'one', 2: 2}
And from a mapping:
dict(mapping) -> new dictionary initialized from a mapping object's
(key, value) pairs
Example:
>>> dict({1:'one',2:2})
{1: 'one', 2: 2}
While that may not seem like much (a dict from a dict literal) keep in mind that Counter and defaultdict are mappings and this is how you would covert one of those to a dict:
>>> from collections import Counter
>>> Counter('aaaaabbbcdeffff')
Counter({'a': 5, 'f': 4, 'b': 3, 'c': 1, 'e': 1, 'd': 1})
>>> dict(Counter('aaaaabbbcdeffff'))
{'a': 5, 'c': 1, 'b': 3, 'e': 1, 'd': 1, 'f': 4}
If you read the documentation, you will learn that the dict = {stringA = 1, stringB = 2} notation is valid when the keys are simple strings:
When the keys are simple strings, it is sometimes easier to specify
pairs using keyword arguments:
>>>
>>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'jack': 4098, 'guido': 4127}
Since integers (or other numbers) are not valid keyword arguments, the dict = {1 = 2, 3 = 4} will fail as any call to a function would if you passed an argument to it while naming it with a number:
>>> def test(**kwargs):
... for arg in kwargs:
... print arg, kwargs[arg]
...
>>> test(a=2,b=3)
a 2
b 3
>>> test(1=2, 3=4)
File "<stdin>", line 1
SyntaxError: keyword can't be an expression