How to type annotate dict comprehension? - python

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.

Related

python dictionary replace dict[] operator with dict.get() behavior

my_dict = {'a': 1}
I wish for my_dict['a'] to behave the same as my_dict.get('a')
That way, if I do my_dict['b'], I will not raise an error but get the default None value, the same way you would get it from my_dict.get('b')
In the case of my_dict = {'a': {'b': 2}} I could do my_dict['a']['b'] and it would act as my_dict.get('a').get('b')
When doing my_dict['b'] = 2 it will act same as my_dict.update({'b': 2})
Is it possible to do so that I will not have to inherit from dict?
You can use a collections.defaultdict() object to add a new value to the dictionary each time you try to access a non-existing key:
>>> from collections import defaultdict
>>> d = defaultdict(lambda: None)
>>> d['a'] is None
True
>>> d
defaultdict(<function <lambda> at 0x10f463e18>, {'a': None})
If you don't want the key added, create a subclass of dict that implements the __missing__ method:
class DefaultNoneDict(dict):
def __missing__(self, key):
return None
This explicitly won't add new keys:
>>> d = DefaultNoneDict()
>>> d['a'] is None
True
>>> d
{}
If you wanted to chain .get() calls, you'll have to return an empty dictionary instead, otherwise dict.get(keyA).get(keyB) will fail with an attribute error (the first None returned won't have a .get() method).
Generally speaking, it is better to stick to the default type and be explicit. There is nothing wrong with:
value = some_d.get(outer, {}).get(inner)
Using a defaultdict or a dict subclass with custom __missing__ hook have a downside: they will always produce a default when the key is missing, even when you accidentally produced incorrect keys somewhere else in your code. I often opt for an explicit dict.get() or dict.setdefault() codepath over defaultdict precisely because I want a non-existing key to produce an error in other parts of my project.

How can make all dictionaries inside list of dictionaries Default in Python

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.

Pass dict with non string keywords to function in kwargs

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 }

python SyntaxError with dict(1=...), but {1:...} works

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

what is the 'self.__class__.__missing__' mean

in pinax Userdict.py:
def __getitem__(self, key):
if key in self.data:
return self.data[key]
if hasattr(self.__class__, "__missing__"):
return self.__class__.__missing__(self, key)
why does it do this on self.__class__.__missing__.
thanks
The UserDict.py presented here emulates built-in dict closely, so for example:
>>> class m(dict):
... def __missing__(self, key): return key + key
...
>>> a=m()
>>> a['ciao']
'ciaociao'
just as you can override the special method __missing__ to deal with missing keys when you subclass the built-in dict, so can you override it when you subclass that UserDict.
The official Python docs for dict are here, and they do say:
New in version 2.5: If a subclass of
dict defines a method __missing__(),
if the key key is not present, the
d[key] operation calls that method
with the key key as argument. The
d[key] operation then returns or
raises whatever is returned or raised
by the __missing__(key) call if the
key is not present. No other
operations or methods invoke
__missing__(). If __missing__() is not defined, KeyError is raised.
__missing__() must be a method; it cannot be an instance variable. For an
example, see collections.defaultdict.
If you want to use default values in a dict (aka __missing__), you can check out defaultdict from collections module:
from collections import defaultdict
a = defaultdict(int)
a[1] # -> 0
a[2] += 1
a # -> defaultdict(int, {1: 0, 2: 1})

Categories

Resources