Python itemgetter doesn't return tuples of length 0 or 1.
For example:
from operator import itemgetter
def get_something(keys):
d = {
"a": 1,
"b": 2,
"c": 3
}
return itemgetter(*keys)(d)
print(type(get_something(["a", "b"])))
# <class 'tuple'>
print(type(get_something(["a"])))
# <class 'int'>
print(type(get_something([])))
# TypeError: itemgetter expected 1 arguments, got 0
Is there any good reason that the itemgetter is written this way? And not (1,) for the second last () and for the last?
Is there some other built-in option if I always want to return a tuple/list given the keys?
Is there some other built-in option if I always want to return a
tuple/list given the keys?
just use a comprehension:
[d[k] for k in keys]
In context:
from operator import itemgetter
def get_something(keys):
d = {
"a": 1,
"b": 2,
"c": 3
}
return [d[k] for k in keys]
print(get_something(["a", "b"]))
#[1, 2]
print(get_something(["a"]))
#[1]
print(get_something([]))
#[]
Part of your confusion comes from the fact that your get_something() func takes a single argument (expected to be an iterable) and unpacks it when passing it to itemgetter(). This results in the return value of get_something() not being "symetric" with it's arguments.
If you defined get_something() to use varargs instead (as itemgetter() does) :
def get_something(*keys):
d = {
"a": 1,
"b": 2,
"c": 3
}
return itemgetter(*keys)(d)
the return values would be more consistant with the arguments, ie:
# ask for 3 keys, get 3 values:
>>> get_something("a", "b", "c")
(1, 2, 3)
# ask for 2 keys, get 2 values:
>>> get_something("a", "b")
(1, 2)
# ask for one key, get 1 value
>>> get_something("a")
1
# all of this with tuple unpacking in mind:
a, b = get_something("a", "b")
a = get_something("a")
Now the point is that few people would bother using itemgetter() to implement your get_something function - itemgetter has mainly been designed to be used as a callback for sorted() and like functions / methods (where it's current behaviour makes sense), and get_something would more canonically be implemented with a list expression ie:
def get_something(keys):
d = {
"a": 1,
"b": 2,
"c": 3
}
return [d[k] for k in keys]
which would take an iterable and return a (possibly empty) list.
This behavior is documented in the docs (emphasis is mine):
Return a callable object that fetches item from its operand using the
operand’s __getitem__() method. If multiple items are specified,
returns a tuple of lookup values
itemgetter does not decide the return type, it is the operand's __getitem__() method.
Wouldn't it be easier/better
"better" is subjective. You can always wrap itemgetter:
def get_something(keys):
def my_itemgetter():
r = itemgetter(*keys)(d)
return (r,) if type(r) is not tuple else r
d = {
"a": 1,
"b": 2,
"c": 3
}
return my_itemgetter()
Related
Im trying to create a function that take unknown number of arguments (dictionaries) to merge them in one. Here is my sketch:
weight = {"sara": 60, "nick": 79, "sem": 78, "ida": 56, "kasia": 58, "slava": 95}
height = { "a" : 1, "b": 2, "c":3 }
width = {"u": "long", "q": 55, "qw": "erre", 30: "34"}
a = {10:20, 20:"a"}
def merge(**dict):
new_dict = {}
for x in dict:
for a, b in x.items():
new_dict[a] = b
return new_dict
print(merge(weight, height, width, a))
And I got error:
TypeError: merge() takes 0 positional arguments but 4 were given
Why?
First note: dict is a bad name for an argument as it is already the name of a type.
When you use ** in the argument list for a function it is slurping up any keyword arguments you haven't explicitly listed. Similarly, a parameter with a single * is slurping up any extra positional arguments not explicitly named.
Consider:
>>> def foo(bar, **baz): return (bar, baz)
...
>>> foo(42, wooble=42)
(42, {'wooble': 42})
>>> foo(bar=42, wooble=42)
(42, {'wooble': 42})
>>> foo(bar=42, wooble=42, hello="world")
(42, {'wooble': 42, 'hello': 'world'})
>>>
If you wish to take any number of dictionaries as arguments, you'd use:
def merge(*dicts):
...
As dicts will now slurp up any number of dictionaries passed in.
Change def merge(**dict): to def merge(*dict): and it is working. Avoid naming it dict since it is a keyword in python.
If you want to pass a list of dicts as a single argument you have to do this:
def foo(*dicts)
Anyway you SHOULDN'T name it *dict, since you are overwriting the dict class.
In Python you can pass all the arguments as a list with the * operator...
def foo(*args)
...and as a dict with the ** operator
def bar(**kwargs)
For example:
>>> foo(1, 2, 3, 4) # args is accessible as a list [1, 2, 3, 4]
>>> bar(arg1='hello', arg2='world') # kwargs is accessible as a dict {'arg1'='hello', 'arg2'='world'}
In your case you can edit the prototype of you function this way:
def merge(*dicts):
So I have a reference dict
ref = {"a": 1, "b": 2}
and a list of dicts
dict_list = [{"a": 1, "b": 2, "c": "success!"}, {"a": 1, "b": 3, "c": "nope!"}]
What I want is to find the dict in dict_list which matches the reference, and returns the value c (i.e. "success!"). I was able to do this, but I'm not a fan of this solution at all:
In [7]: import pandas as pd
...: def f(ref, dict_list):
...: df = pd.DataFrame.from_records(dict_list)
...: return df.loc[(df["a"] == ref["a"]) & (df["b"] == ref["b"])].c[0]
...:
...: f(ref, dict_list)
Out[7]: 'success!'
if anyone has anything more elegant (ideally in pure python) would be great!
Use next:
>>> next((x['c'] for x in dict_list if ref.items() < x.items()))
'success!'
>>>
Or:
>>> next((x['c'] for x in dict_list if dict(x, **ref) == x))
'success!'
>>>
This will get the key c when the ref dictionary is a subset of the iterator dictionaries. This won't only work for a and b keys, it works for all cases.
In Python 3, to check if a dictionary is a subset of another, you can use the < operator.
For the second case, since dictionaries can't have additional keys, it joins the two dictionaries and determines whether it's the same as the original dictionary iterator, if so, it yields the c key from iterator x.
Edit:
As #sj95126 mentioned, as of Python 3.9 you could use the concatenation method:
>>> next((x['c'] for x in dict_list if x | ref == x))
'success!'
>>>
This is the same logic as dict(x, **ref).
Edit 2:
Obviously, you could do:
>>> next((x['c'] for x in dict_list if [x['a'], x['b']] == list(ref.values())))
'success!'
>>>
For only a and b keys.
Python itemgetter doesn't return tuples of length 0 or 1.
For example:
from operator import itemgetter
def get_something(keys):
d = {
"a": 1,
"b": 2,
"c": 3
}
return itemgetter(*keys)(d)
print(type(get_something(["a", "b"])))
# <class 'tuple'>
print(type(get_something(["a"])))
# <class 'int'>
print(type(get_something([])))
# TypeError: itemgetter expected 1 arguments, got 0
Is there any good reason that the itemgetter is written this way? And not (1,) for the second last () and for the last?
Is there some other built-in option if I always want to return a tuple/list given the keys?
Is there some other built-in option if I always want to return a
tuple/list given the keys?
just use a comprehension:
[d[k] for k in keys]
In context:
from operator import itemgetter
def get_something(keys):
d = {
"a": 1,
"b": 2,
"c": 3
}
return [d[k] for k in keys]
print(get_something(["a", "b"]))
#[1, 2]
print(get_something(["a"]))
#[1]
print(get_something([]))
#[]
Part of your confusion comes from the fact that your get_something() func takes a single argument (expected to be an iterable) and unpacks it when passing it to itemgetter(). This results in the return value of get_something() not being "symetric" with it's arguments.
If you defined get_something() to use varargs instead (as itemgetter() does) :
def get_something(*keys):
d = {
"a": 1,
"b": 2,
"c": 3
}
return itemgetter(*keys)(d)
the return values would be more consistant with the arguments, ie:
# ask for 3 keys, get 3 values:
>>> get_something("a", "b", "c")
(1, 2, 3)
# ask for 2 keys, get 2 values:
>>> get_something("a", "b")
(1, 2)
# ask for one key, get 1 value
>>> get_something("a")
1
# all of this with tuple unpacking in mind:
a, b = get_something("a", "b")
a = get_something("a")
Now the point is that few people would bother using itemgetter() to implement your get_something function - itemgetter has mainly been designed to be used as a callback for sorted() and like functions / methods (where it's current behaviour makes sense), and get_something would more canonically be implemented with a list expression ie:
def get_something(keys):
d = {
"a": 1,
"b": 2,
"c": 3
}
return [d[k] for k in keys]
which would take an iterable and return a (possibly empty) list.
This behavior is documented in the docs (emphasis is mine):
Return a callable object that fetches item from its operand using the
operand’s __getitem__() method. If multiple items are specified,
returns a tuple of lookup values
itemgetter does not decide the return type, it is the operand's __getitem__() method.
Wouldn't it be easier/better
"better" is subjective. You can always wrap itemgetter:
def get_something(keys):
def my_itemgetter():
r = itemgetter(*keys)(d)
return (r,) if type(r) is not tuple else r
d = {
"a": 1,
"b": 2,
"c": 3
}
return my_itemgetter()
I am looking to create a function that will check if multiple keys are present in a dictionary and return a value that indicates which keys were present. I know I could write something like:
def param_checker(parameters):
if 'foo' in parameters:
if 'bar' in parameters:
if 'qux' in parameters:
return 0
else:
return 1
elif 'baz' in parameters:
if 'qux' in parameters:
return 2
else:
return 3
else:
return 4
elif 'quxx' in parameters:
if 'bar' in parameters:
return 5
elif 'qux' in parameters:
if 'baz' in parameters:
return 6
else:
return 7
else:
return 8
else:
return 9
However, the above example is both messy and clunky. The complexity also scales with the number of parameters you need to check to return a value. For example, I needed 3 layers of if conditions to check the keys foo, bar, and qux. I know I could chain the if conditions inline, but it is still messy and clunky.
This is somewhat similar to Python check if list of keys exist in dictionary. The big difference here is that instead of checking if they all exist or not, I am looking to return a value indicating what combination of keys exist.
Note: I did not write out all possible combinations in the provided sample code, as that would be tedious when I am just trying to illustrate the problem.
Below is a example of invoking the function to check which keys are present.
d = dict()
d['foo'] = 'blah'
d['bar'] = 'blah'
val = param_checker(d)
# val represents a unique value indicating which keys are present in dictionary d
I will know ahead of time what keys could be in the dictionary, so I can have some sort of list of possible keys, like:
keys = ["foo", "bar", "baz", "qux", "quxx"]
I am hoping to build a function that can check N keys and return a unique value indicating which ones where present. How can I efficiently do this?
keyseq = ('foo', 'bar', 'quux')
keymap = {key: 1 << e for (e, key) in enumerate(keyseq)}
return sum(keymap[key] for key in keymap if key in D)
Something like this (assuming Python 3)?
from itertools import compress
queries = ["a", "b", "c", "d"]
in_dict = [q in d for q in queries]
existing_keys = compress(queries, in_dict)
You can of course wrap this in a function where queries become the input parameter and d is the input dictionary.
Make a set of your desired keys.
Make a set of the dict's keys.
Take the intersection. Count.
master = {
"foo": 1,
"bar": 5,
"qux": 18,
"lemon": -6,
"quxx": "How boring are examples?",
"dog": False
}
tag = {
"foo", "bar", "baz", "qux", "quxx"
}
master_set = set(master.keys())
found = master_set.intersection(tag)
print (len(found), found)
Output:
4 {'quxx', 'foo', 'qux', 'bar'}
You can try this:
keys = ["foo", "bar", "baz", "qux", "quxx"]
counter = {i:d.keys().count(i) for i in keys}
I was looking for a way to "unpack" a dictionary in a generic way and found a relevant question (and answers) which explained various techniques (TL;DR: it is not too elegant).
That question, however, addresses the case where the keys of the dict are not known, the OP anted to have them added to the local namespace automatically.
My problem is possibly simpler: I get a dict from a function and would like to dissecate it on the fly, knowing the keys I will need (I may not need all of them every time). Right now I can only do
def myfunc():
return {'a': 1, 'b': 2, 'c': 3}
x = myfunc()
a = x['a']
my_b_so_that_the_name_differs_from_the_key = x['b']
# I do not need c this time
while I was looking for the equivalent of
def myotherfunc():
return 1, 2
a, b = myotherfunc()
but for a dict (which is what is returned by my function). I do not want to use the latter solution for several reasons, one of them being that it is not obvious which variable corresponds to which returned element (the first solution has at least the merit of being readable).
Is such operation available?
If you really must, you can use an operator.itemgetter() object to extract values for multiple keys as a tuple:
from operator import itemgetter
a, b = itemgetter('a', 'b')(myfunc())
This is still not pretty; I'd prefer the explicit and readable separate lines where you first assign the return value, then extract those values.
Demo:
>>> from operator import itemgetter
>>> def myfunc():
... return {'a': 1, 'b': 2, 'c': 3}
...
>>> itemgetter('a', 'b')(myfunc())
(1, 2)
>>> a, b = itemgetter('a', 'b')(myfunc())
>>> a
1
>>> b
2
You could also use map:
def myfunc():
return {'a': 1, 'b': 2, 'c': 3}
a,b = map(myfunc().get,["a","b"])
print(a,b)
In addition to the operator.itemgetter() method, you can also write your own myotherfunc(). It takes list of the required keys as an argument and returns a tuple of their corresponding value.
def myotherfunc(keys_list):
reference_dict = myfunc()
return tuple(reference_dict[key] for key in keys_list)
>>> a,b = myotherfunc(['a','b'])
>>> a
1
>>> b
2
>>> a,c = myotherfunc(['a','c'])
>>> a
1
>>> c
3