variable - varargs python




Modo corretto per usare** kwargs in Python (9)

È possibile passare un valore predefinito a get() per le chiavi che non sono nel dizionario:

self.val2 = kwargs.get('val2',"default value")

Tuttavia, se prevedi di utilizzare un particolare argomento con un particolare valore predefinito, perché non utilizzare gli argomenti con nome in primo luogo?

def __init__(self, val2="default value", **kwargs):

Qual è il modo corretto di usare **kwargs in Python quando si tratta di valori predefiniti?

kwargs restituisce un dizionario, ma qual è il modo migliore per impostare i valori predefiniti o ce n'è uno? Dovrei semplicemente accedervi come un dizionario? Usa la funzione get?

class ExampleClass:
    def __init__(self, **kwargs):
        self.val = kwargs['val']
        self.val2 = kwargs.get('val2')

Una domanda semplice, ma su cui non riesco a trovare buone risorse. Le persone fanno in modo diverso nel codice che ho visto ed è difficile sapere cosa usare.


@AbhinavGupta e @Steef hanno suggerito di utilizzare update() , che ho trovato molto utile per elaborare elenchi di argomenti di grandi dimensioni:

args.update(kwargs)

Cosa succede se vogliamo verificare che l'utente non abbia trasmesso argomenti spuri / non supportati? @VinaySajip ha sottolineato che pop() può essere usato per elaborare iterativamente l'elenco di argomenti. Quindi, qualsiasi argomento avanzato è spurio. Bello.

Ecco un altro modo per farlo, che mantiene la semplice sintassi dell'uso di update() :

# kwargs = dictionary of user-supplied arguments
# args = dictionary containing default arguments

# Check that user hasn't given spurious arguments
unknown_args = user_args.keys() - default_args.keys()
if unknown_args:
    raise TypeError('Unknown arguments: {}'.format(unknown_args))

# Update args to contain user-supplied arguments
args.update(kwargs)

unknown_args è un set contenente i nomi degli argomenti che non si verificano nei valori predefiniti.


Faresti

self.attribute = kwargs.pop('name', default_value)

o

self.attribute = kwargs.get('name', default_value)

Se usi pop , puoi controllare se ci sono valori spuri inviati e prendere l'azione appropriata (se esiste).


Mentre la maggior parte delle risposte sta dicendo che, ad esempio,

def f(**kwargs):
    foo = kwargs.pop('foo')
    bar = kwargs.pop('bar')
    ...etc...

equivale a"

def f(foo=None, bar=None, **kwargs):
    ...etc...

questo non è vero. In quest'ultimo caso, f può essere chiamato come f(23, 42) , mentre il primo caso accetta solo argomenti con nome - nessuna chiamata di posizione. Spesso si desidera consentire al chiamante la massima flessibilità e quindi la seconda forma, come la maggior parte delle risposte asserisce, è preferibile: ma non è sempre così. Quando si accettano molti parametri opzionali di cui in genere solo pochi vengono passati, può essere un'ottima idea (evitare incidenti e codice illeggibile nei siti di chiamata!) Per forzare l'uso degli argomenti con nome - threading.Thread è un esempio. Il primo modulo è come lo si implementa in Python 2.

L'idioma è così importante che in Python 3 ora ha una speciale sintassi di supporto: ogni argomento dopo un singolo * nella def firma è solo parola chiave, cioè non può essere passato come argomento posizionale, ma solo come un nome. Quindi in Python 3 potresti codificare quanto sopra:

def f(*, foo=None, bar=None, **kwargs):
    ...etc...

Infatti, in Python 3 puoi anche avere argomenti solo per parole chiave che non sono facoltativi (quelli senza un valore predefinito).

Tuttavia, Python 2 ha ancora lunghi anni di vita produttiva, quindi è meglio non dimenticare le tecniche e gli idiomi che ti permettono di implementare in Python 2 importanti idee progettuali direttamente supportate nel linguaggio in Python 3!


Poiché **kwargs viene utilizzato quando il numero di argomenti è sconosciuto, perché non farlo?

class Exampleclass(object):
  def __init__(self, **kwargs):
    for k in kwargs.keys():
       if k in [acceptable_keys_list]:
          self.__setattr__(k, kwargs[k])

Potresti fare qualcosa di simile

class ExampleClass:
    def __init__(self, **kwargs):
        arguments = {'val':1, 'val2':2}
        arguments.update(kwargs)
        self.val = arguments['val']
        self.val2 = arguments['val2']

Seguendo il suggerimento di setattr sull'uso di setattr :

class ExampleClass(object):
    __acceptable_keys_list = ['foo', 'bar']

    def __init__(self, **kwargs):
        [self.__setattr__(key, kwargs.get(key)) for key in self.__acceptable_keys_list]

Questa variante è utile quando ci si aspetta che la classe abbia tutti gli elementi nel nostro elenco acceptable .


Suggerisco qualcosa di simile

def testFunc( **kwargs ):
    options = {
            'option1' : 'default_value1',
            'option2' : 'default_value2',
            'option3' : 'default_value3', }

    options.update(kwargs)
    print options

testFunc( option1='new_value1', option3='new_value3' )
# {'option2': 'default_value2', 'option3': 'new_value3', 'option1': 'new_value1'}

testFunc( option2='new_value2' )
# {'option1': 'default_value1', 'option3': 'default_value3', 'option2': 'new_value2'}

E poi usa i valori come preferisci

dictionaryA.update(dictionaryB) aggiunge il contenuto del dictionaryB al dictionaryA sovrascrivendo qualsiasi chiave duplicata.


Usare ** kwargs e valori predefiniti è facile. A volte, tuttavia, non dovresti usare ** kwargs in primo luogo.

In questo caso, non stiamo davvero facendo il miglior uso di ** kwargs.

class ExampleClass( object ):
    def __init__(self, **kwargs):
        self.val = kwargs.get('val',"default1")
        self.val2 = kwargs.get('val2',"default2")

Quanto sopra è un "perché preoccuparsi?" dichiarazione. È lo stesso di

class ExampleClass( object ):
    def __init__(self, val="default1", val2="default2"):
        self.val = val
        self.val2 = val2

Quando utilizzi ** kwargs, intendi che una parola chiave non è solo facoltativa, ma condizionale. Esistono regole più complesse rispetto ai semplici valori predefiniti.

Quando utilizzi ** kwargs, di solito intendi qualcosa di più simile al seguente, in cui le impostazioni predefinite semplici non si applicano.

class ExampleClass( object ):
    def __init__(self, **kwargs):
        self.val = "default1"
        self.val2 = "default2"
        if "val" in kwargs:
            self.val = kwargs["val"]
            self.val2 = 2*self.val
        elif "val2" in kwargs:
            self.val2 = kwargs["val2"]
            self.val = self.val2 / 2
        else:
            raise TypeError( "must provide val= or val2= parameter values" )




kwargs