Python: ¿Por qué el tipo de devolución de itemgetter no es consistente?

El itemgetter Python no devuelve tuplas de longitud 0 o 1. Por ejemplo:

 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"]))) #  print(type(get_something(["a"]))) #  print(type(get_something([]))) # TypeError: itemgetter expected 1 arguments, got 0 

¿Hay alguna otra opción integrada si siempre deseo devolver una tupla / lista dadas las claves?

solo usa una comprensión

 [d[k] for k in keys] 

En contexto:

 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([])) #[] 

Parte de su confusión proviene del hecho de que su función get_something() toma un solo argumento (se espera que sea un iterable) y lo desempaqueta cuando se lo pasa a itemgetter() . Esto hace que el valor de retorno de get_something() no sea “simétrico” con sus argumentos.

Si get_something() para usar varargs en su lugar (como hace itemgetter() ):

 def get_something(*keys): d = { "a": 1, "b": 2, "c": 3 } return itemgetter(*keys)(d) 

los valores de retorno serían más consistentes con los argumentos, es decir:

 # 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") 

Ahora el punto es que pocas personas se molestarán en usar el itemgetter() para implementar su función get_something ; el itemgetter ha sido diseñado principalmente para ser usado como callback para funciones / métodos sorted() y similares (donde el comportamiento actual tiene sentido), y get_something se implementaría más canónicamente con una expresión de lista, es decir:

 def get_something(keys): d = { "a": 1, "b": 2, "c": 3 } return [d[k] for k in keys] 

que tomaría un iterable y devolvería una lista (posiblemente vacía).

Este comportamiento está documentado en los documentos (el énfasis es mío):

Devuelva un objeto invocable que recupera un elemento de su operando utilizando el método __getitem__() del operando. Si se especifican varios elementos, devuelve una tupla de valores de búsqueda

itemgetter no decide el tipo de retorno, es el método __getitem__() del operando.

¿No sería más fácil / mejor?

“mejor” es subjetivo. Siempre puedes envolver 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()