pass - varargs python




Perché usare** kwargs in python? Quali sono alcuni vantaggi del mondo reale rispetto all'utilizzo degli argomenti con nome? (6)

Ci sono due casi comuni:

Primo: stai avvolgendo un'altra funzione che accetta un numero di argomenti di parole chiave, ma li passerai semplicemente:

def my_wrapper(a, b, **kwargs):
    do_something_first(a, b)
    the_real_function(**kwargs)

Secondo: sei disposto ad accettare qualsiasi argomento di parole chiave, ad esempio, per impostare attributi su un oggetto:

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'

Vengo da uno sfondo in lingue statiche. Qualcuno può spiegare (idealmente attraverso l'esempio) i vantaggi del mondo reale nell'usare ** kwargs rispetto agli argomenti con nome ?

Per me sembra solo rendere la funzione più ambigua. Grazie.


Ecco un esempio, l'ho usato in CGI Python. Ho creato una classe che ha richiesto **kwargs alla funzione __init__ . Questo mi ha permesso di emulare il DOM sul lato server con le classi:

document = Document()
document.add_stylesheet('style.css')
document.append(Div(H1('Imagist\'s Page Title'), id = 'header'))
document.append(Div(id='body'))

L'unico problema è che non puoi fare quanto segue, perché la class è una parola chiave Python.

Div(class = 'foo')

La soluzione è accedere al dizionario sottostante.

Div(**{'class':'foo'})

Non sto dicendo che questo è un uso "corretto" della funzionalità. Quello che sto dicendo è che ci sono tutti i tipi di modi in cui le funzioni come questa possono essere utilizzate.


Esempi del mondo reale:

Decoratori: di solito sono generici, quindi non è possibile specificare gli argomenti in anticipo:

def decorator(old):
    def new(*args, **kwargs):
        # ...
        return old(*args, **kwargs)
    return new

Luoghi in cui vuoi fare magie con un numero sconosciuto di argomenti per le parole chiave. L'ORM di Django lo fa, ad esempio:

Model.objects.filter(foo__lt = 4, bar__iexact = 'bar')

Potresti voler accettare argomenti con nome quasi arbitrario per una serie di motivi - ed è quello che ti permette di fare il modulo **kw .

Il motivo più comune è quello di passare gli argomenti direttamente ad un'altra funzione che stai eseguendo (i decoratori sono un caso di questo, ma LONTANO dall'unico!) - in questo caso, **kw allenta l'accoppiamento tra wrapper e wrappee, poiché il wrapper non deve conoscere o preoccuparsi di tutti gli argomenti del wrappee. Ecco un'altra ragione completamente diversa:

d = dict(a=1, b=2, c=3, d=4)

se tutti i nomi dovessero essere conosciuti in anticipo, ovviamente questo approccio non potrebbe esistere, giusto? E a proposito, quando applicabile, preferisco di gran lunga questo modo di fare un dict le cui chiavi sono stringhe letterali per:

d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}

semplicemente perché quest'ultimo è abbastanza punteggiatura-pesante e quindi meno leggibile.

Quando nessuno degli eccellenti motivi per accettare **kwargs applica, allora non accettarlo: è così semplice. IOW, se non ci sono buone ragioni per consentire al chiamante di passare arg argentati con nomi arbitrari, non permettere che ciò accada, basta evitare di inserire un modulo di **kw alla fine della firma della funzione nella dichiarazione def .

Per quanto riguarda l' utilizzo di **kw in una chiamata, che ti permette di mettere insieme l'esatto insieme di argomenti con nome che devi passare, ognuno con valori corrispondenti, in un dict, indipendentemente da un singolo call point, quindi usare quel dettato alla singola chiamata punto. Confrontare:

if x: kw['x'] = x
if y: kw['y'] = y
f(**kw)

a:

if x:
  if y:
    f(x=x, y=y)
  else:
    f(x=x)
else:
  if y:
    f(y=y)
  else:
    f()

Anche con solo due possibilità (e del tipo molto più semplice!), La mancanza di **kw è già pronta a rendere la seconda opzione assolutamente insostenibile e intollerabile - immagina solo come si sviluppa quando ci sono una mezza dozzina di possibilità, forse in un po 'più ricca interazione ... senza **kw , la vita sarebbe un inferno assoluto in tali circostanze!


Un'altra ragione per cui potresti voler usare **kwargs (e *args ) è se stai estendendo un metodo esistente in una sottoclasse. Vuoi passare tutti gli argomenti esistenti sul metodo della superclasse, ma vuoi assicurarti che la tua classe continui a funzionare anche se la firma cambia in una versione futura:

class MySubclass(Superclass):
    def __init__(self, *args, **kwargs):
        self.myvalue = kwargs.pop('myvalue', None)
        super(MySubclass, self).__init__(*args, **kwargs)

**kwargs sono buoni se non si conosce in anticipo il nome dei parametri. Ad esempio il costruttore di dict li usa per inizializzare le chiavi del nuovo dizionario.

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}




kwargs