Digamos que tengo un código:

def test(a, b, **kwargs):
    print(kwargs)

l = {'a': 0, 'c': 1, 'foo': 2, 'bar': 3}

Lo que quiero hacer es pasar el diccionario desempaquetado a la función, pero asignar su clave c al parámetro b, mientras conserva cualquier otra clave que no corresponda directamente a un parámetro en kwargs, por lo que la función debería generar {'foo': 2, 'bar': 3}. Si hago test(b=l['c'], **l), la clave c permanece en kwargs, y la salida se ve así: {'foo': 2, 'bar': 3, 'c': 1}. test(**l), obviamente, se bloquea con un error - test() missing 1 required positional argument: 'b'.

¿Cómo es posible hacer esto?

1
Dariush 28 oct. 2017 a las 23:31

3 respuestas

La mejor respuesta

Elimine la clave c y agregue b:

def test(a, b, **kwargs):
    print(kwargs)

l = {'a': 0, 'c': 1, 'foo': 2, 'bar': 3}

l2 = l.copy()
l2['b'] = l2['c']
del l2['c']

test(**l2)

Salida:

{'foo': 2, 'bar': 3}

https://repl.it/NXd2/0

0
Eugene Lisitsky 28 oct. 2017 a las 20:39

Para casos más sofisticados en los que sería necesario asignar / ajustar varias claves sin mutar el diccionario de entrada inicial, considere usar decoradores :

import functools

def map_keys_decorator():
    def decorate(func):

        @functools.wraps(func)
        def mapped_test(*args, **kwargs):
            kwargs = dict(kwargs)
            kwargs['b'] = kwargs.pop('c')   # here you can add another additonal logic
            return func(**kwargs)
        return mapped_test

    return decorate

@map_keys_decorator()
def test(a, b, **kwargs):
    print(kwargs)

l = {'a': 0, 'c': 1, 'foo': 2, 'bar': 3}
test(**l)    # {'foo': 2, 'bar': 3}

print(l)     # {'foo': 2, 'bar': 3, 'c': 1, 'a': 0}
1
RomanPerekhrest 28 oct. 2017 a las 21:03

Lo que quieres no es posible. Simplemente manipule su diccionario antes de pasarlo a la llamada:

b = l.pop('c')
test(b=b, **l)

O

l['b'] = l.pop('c')
test(**l)

O

test(**{'b' if k == 'c' else k: v for k, v in l.items()})

Todo lo cual pasa en un diccionario a la sintaxis ** que no tiene una clave c.

3
Martijn Pieters 28 oct. 2017 a las 20:35