Próbuję użyć szablonu Słowo kluczowe Args dla wygody w wywołaniu funkcji (za pośrednictwem argumentów słów kluczowych) podczas gdy będąc w stanie zastąpić niektóre argumenty.

Na przykład, jeśli zaczniemy od modułu Mymod zawierający linię template_kvps = {'a': 1, 'b': 2}

Mogę po prostu:

import mymod

def func(**kwargs):
    pass

func(**mymod.template_kvps)

Następnie mogę uzyskać dostęp do mojego template_kvps w func(). Ale chcę być w stanie przejść inną wartość dla a z minimalnym napowietrznym.

Wszystko, o czym mogę pomyśleć, ma zmienić słownik przed połączeniem: kvps = {**template_kvps, 'a': 3}; func(**kvps), ale to dwa razy więcej linii i używam tej funkcji kilka razy w każdym z 1000 skryptów testowych.

Idealnie mi się przedefiniować func, dzięki czemu mogę zrobić coś takiego jak func(**mymod.template_kvps, a=3), ale tak jak jest, błędy Pythona z czymś o powtarzających się parametrach.

BTW Cieszę się, że rozważę zmianę formatu template_kvps.

edytuj (przeniesie się do odpowiedzi w pewnym momencie) Mogę zamiast tego użyć metody opakowania

def func_template(a=1, b=2):
    func(a, b)

func_template(a=3)
11
joel 27 czerwiec 2017, 18:43

6 odpowiedzi

Najlepsza odpowiedź

Do tego celu można użyć wbudowanego dict. Akceptuje kolejne dyktowanie jako argument i dodatkowe pary wartości kluczowych jako argumenty słów kluczowych (które mają pierwszeństwo przed wartościami w pozostałych dict).

W ten sposób możesz utworzyć zaktualizowany słownik przez dict(template_vars, a=1).

Możesz rozwinąć ten dykt jako argumenty słowa kluczowego: func(**dict(...)).

Tak jak nie ma potrzeby zmiany podpisu swojej funkcji i możesz zaktualizować / dodać dowolną liczbę par kluczowych par, ile chcesz.

7
a_guest 27 czerwiec 2017, 16:21

Możesz mieć funkcję, która aktualizuje dowolne wartości, które chcesz zmienić. (Edytowane tak, że oryginalny słownik nie zostanie zmodyfikowany.)

import mymod

def replace(dict1, dict2):
  ans = dict1.copy()
  ans.update(dict2)
  return ans

def func(**kwargs):
  pass

func(replace(mymod.template_kvps, {'a':3}))
0
Carl Shiles 29 czerwiec 2017, 14:11

Zmieniłbym argument szablonu być argumentem pozycyjnym, przy użyciu kluczy, które zostaną nadpisane jako **kwargs.

def func(template_dict, **kwargs):
    updated_dict = {**template_dict, **kwargs}
    return updated_dict

print(func({'a':1, 'b':2}, a=3)) # {'a': 3, 'b': 2}

Należy pamiętać, że składnia podwójna rozpakowań wymaga Pythona 3.5+. Na starszych wersjach, użyj:

updated_dict = template_dict.copy()
updated_dict.update(kwargs)
0
Jared Goguen 27 czerwiec 2017, 16:21

To powinno działać

func(a=3, **{key: value for key, value in mymod.template_kvps.items() if key != 'a')})

Jednak sugerowałbym napisać kolejną linię kodu, a nie uwięzić go.

0
hspandher 27 czerwiec 2017, 15:56

Rozważałbym funkcje Dekoratorów, ponieważ utrzymuje składnię głównie tak samo, jak ożądałeś. Wdrożenie wyglądałoby coś takiego:

def combine(func):
  def wrapper(*args, **kwargs):
    fargs = {}
    if args and isinstance( args[0], dict ):
      fargs = args[0].copy()
    fargs.update(kwargs)
    return func(**fargs)
  return wrapper


@combine
def funky(**kwargs):
  for k,v in kwargs.iteritems():
    print "%s: %s" % (k, v)

# All of these work as expected
funky( {'a': 1, 'b': 2}, a=3 )
funky( {'a': 1, 'b': 2} )
funky( a=3 )
1
Clarus 27 czerwiec 2017, 16:37

Możesz to zrobić w jednej linii w ten sposób:

func(**{**mymod.template_kvps, 'a': 3})

Ale to może nie być oczywiste na pierwszy rzut oka, ale jest tak oczywiste jak to, co robiłeś wcześniej.

Sugerowałbym, że posiadanie wielu szablonów (np {x0}}), ale to zależało od konkretnego przypadku użycia:

func(**mymod.template_kvps_without_a, a=3)
2
Artyer 27 czerwiec 2017, 16:00