Dlaczego warto używać * * kwargs w Pythonie? Jakie są realne zalety używania nazwanych argumentów?
Pochodzę z tła w językach statycznych. Czy ktoś może wyjaśnić (najlepiej na przykładzie) zalety używania **kwargs nad nazwanymi argumentami ?
Dla mnie to tylko sprawia, że wywołanie funkcji jest bardziej niejednoznaczne. Dzięki.
8 answers
Prawdziwe przykłady:
Dekoratory-są zazwyczaj ogólne, więc nie można podać argumentów z góry:
def decorator(old):
def new(*args, **kwargs):
# ...
return old(*args, **kwargs)
return new
Miejsca, w których chcesz zrobić magię z nieznaną liczbą argumentów słów kluczowych. ORM Django tak robi, np.:
Model.objects.filter(foo__lt = 4, bar__iexact = 'bar')
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-12 18:50:27
Możesz chcieć zaakceptować niemal dowolne argumenty nazwane z wielu powodów - i na to pozwala formularz **kw
.
Najczęstszym powodem jest przekazywanie argumentów bezpośrednio do innej funkcji, którą zawijasz (dekoratory są jednym z takich przypadków, ale nie jedynym!) -- w tym przypadku **kw
rozluźnia połączenie pomiędzy wrapperem a wrappee, ponieważ wrapper nie musi znać ani przejmować się wszystkimi argumentami wrappee. Oto kolejny, zupełnie inny powód:
d = dict(a=1, b=2, c=3, d=4)
Jeśli wszystkie nazwiska muszą być znane z góry, to oczywiście takie podejście nie mogłoby istnieć, prawda? A btw, jeśli ma to zastosowanie, zdecydowanie wolę ten sposób robienia dict, którego klucze są literalnymi ciągami do:
d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
Po prostu dlatego, że ten ostatni jest dość ciężki w interpunkcji, a przez to mniej czytelny.
Jeśli nie ma zastosowania żaden z doskonałych powodów do zaakceptowania **kwargs
, nie Akceptuj tego: to takie proste. IOW, jeśli nie ma dobrego powodu, aby pozwolić na aby przekazać dodatkowe args o dowolnych nazwach, nie pozwól na to-unikaj umieszczania formularza **kw
na końcu podpisu funkcji w instrukcji def
.
Jak dla za pomocą **kw
w wywołaniu, które pozwala połączyć dokładny zestaw nazwanych argumentów, które musisz przekazać, każdy z odpowiednimi wartościami, w dict, niezależnie od pojedynczego punktu wywoławczego, a następnie użyć tego dict w pojedynczym punkcie wywoławczym. Porównaj:
if x: kw['x'] = x
if y: kw['y'] = y
f(**kw)
Do:
if x:
if y:
f(x=x, y=y)
else:
f(x=x)
else:
if y:
f(y=y)
else:
f()
Nawet z zaledwie dwoma możliwościami (i bardzo najprostszego rodzaju!), brak **kw
sprawia, że druga opcja jest absolutnie nie do utrzymania i nie do zniesienia-wyobraź sobie, jak to się rozgrywa, gdy jest pół tuzina możliwości, być może w nieco bogatszej interakcji... Bez **kw
, życie byłoby absolutnym piekłem w takich okolicznościach!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-03 16:53:59
Innym powodem, dla którego warto użyć **kwargs
(i *args
) jest rozszerzenie istniejącej metody w podklasie. Chcesz przekazać wszystkie istniejące argumenty do metody superclass, ale chcesz mieć pewność, że twoja klasa nadal będzie działać, nawet jeśli podpis ulegnie zmianie w przyszłej wersji:
class MySubclass(Superclass):
def __init__(self, *args, **kwargs):
self.myvalue = kwargs.pop('myvalue', None)
super(MySubclass, self).__init__(*args, **kwargs)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-12 19:15:33
Istnieją dwa typowe przypadki:
Po pierwsze: owijasz inną funkcję, która pobiera kilka argumentów słów kluczowych, ale po prostu je przekazujesz:
def my_wrapper(a, b, **kwargs):
do_something_first(a, b)
the_real_function(**kwargs)
Po drugie: możesz zaakceptować dowolny argument ze słowem kluczowym, na przykład, aby ustawić atrybuty na obiekcie:
class OpenEndedObject:
def __init__(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
foo = OpenEndedObject(a=1, foo='bar')
assert foo.a == 1
assert foo.foo == 'bar'
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-12 18:56:22
**kwargs
są dobre, jeśli nie znasz z góry nazwy parametrów. Na przykład konstruktor dict
używa ich do inicjalizacji kluczy nowego słownika.
dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)
In [3]: dict(one=1, two=2)
Out[3]: {'one': 1, 'two': 2}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-12 18:45:42
Oto przykład, którego użyłem w CGI Python. Stworzyłem klasę, która wzięła **kwargs
do funkcji __init__
. To pozwoliło mi emulować DOM po stronie serwera z klasami:
document = Document()
document.add_stylesheet('style.css')
document.append(Div(H1('Imagist\'s Page Title'), id = 'header'))
document.append(Div(id='body'))
Jedynym problemem jest to, że nie możesz wykonać następujących czynności, ponieważ class
jest słowem kluczowym Pythona.
Div(class = 'foo')
Rozwiązaniem jest dostęp do podstawowego słownika.
Div(**{'class':'foo'})
Nie twierdzę, że jest to "poprawne" użycie tej funkcji. Chodzi mi o to, że są różnego rodzaju niewymuszone sposoby w które funkcje tego typu mogą być używane.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-12 21:56:05
A oto kolejny typowy przykład:
MESSAGE = "Lo and behold! A message {message!r} came from {object_} with data {data!r}."
def proclaim(object_, message, data):
print(MESSAGE.format(**locals()))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-13 05:30:16
Jednym z przykładów jest implementacja Python-argument-binders , użyta Tak:
>>> from functools import partial >>> def f(a, b): ... return a+b >>> p = partial(f, 1, 2) >>> p() 3 >>> p2 = partial(f, 1) >>> p2(7) 8
To jest z functools.partial python docs: partial jest 'względnie równoważny' do tego impl:
def partial(func, *args, **keywords): def newfunc(*fargs, **fkeywords): newkeywords = keywords.copy() newkeywords.update(fkeywords) return func(*(args + fargs), **newkeywords) newfunc.func = func newfunc.args = args newfunc.keywords = keywords return newfunc
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:46:52