2013-04-16 11 views
9

einen Fall betrachten, wo ich die kwargs dict innerhalb einer Methode ändern:Veränderlichkeit des ** kwargs Argument in Python

def print_arg(**kwargs): 
    print kwargs.pop('key') 

Wenn ich pop_arg wie dies mit einem Wörterbuch rufen Sie die Methode:

mydict = {'key':'value'} 
print_arg(**mydict) 

wird mydict durch diesen Aufruf geändert?

Ich bin auch interessiert an einer detaillierteren Erklärung der zugrunde liegenden Methode aufrufen Mechanismus, der mydict ändern kann oder nicht.

Antwort

6

Mal sehen:

import dis 
dis.dis(lambda: print_arg(**{'key': 'value'})) 

 

6   0 LOAD_GLOBAL    0 (print_arg) 
       3 BUILD_MAP    1 
       6 LOAD_CONST    1 ('value') 
       9 LOAD_CONST    2 ('key') 
      12 STORE_MAP   
      13 CALL_FUNCTION_KW   0 
      16 RETURN_VALUE   

 

finden lassen, was CALL_FUNCTION_KW tut (ceval.c):

case CALL_FUNCTION_VAR: 
    case CALL_FUNCTION_KW: 
    case CALL_FUNCTION_VAR_KW: 
    { 

     // ... 

     x = ext_do_call(func, &sp, flags, na, nk); 

     // ... 

 

static PyObject * 
ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk) 
{ 
    int nstar = 0; 
    PyObject *callargs = NULL; 
    PyObject *stararg = NULL; 
    PyObject *kwdict = NULL; 
    PyObject *result = NULL; 

    if (flags & CALL_FLAG_KW) {      // if ** is involved 
     kwdict = EXT_POP(*pp_stack);  // get the dict passed with **  
     if (!PyDict_Check(kwdict)) { 
      PyObject *d; 
      d = PyDict_New();      // make a NEW dict 
      if (d == NULL) 
       goto ext_call_fail; 
      if (PyDict_Update(d, kwdict) != 0) { // update it with old 
       // .. fail .. 
       goto ext_call_fail; 
      } 
      Py_DECREF(kwdict); 
      kwdict = d;    // kwdict is now the new dict 
     } 
    } 

    .... 
    result = PyObject_Call(func, callargs, kwdict); // call with new dict 
15

Nein, mydict wird nicht geändert. Kwargs werden in ein neues Wörterbuch entpackt.

Betrachten wir den Fall, wo Sie haben:

def print_arg(key=1,**kwargs): 
    print key 
    print kwargs 

print_arg(**{'key':2,'foo':3,'bar':4}) 

In diesem Fall ist es offensichtlich, dass kwargs eine andere dict ist, als Sie in übergeben, da, wenn es ausgepackt wird, wird es die 'key' Schlüssel fehlt.

+3

Bemerkenswert ausdrücklich, dass es zwar eine flache Kopie sein. –

8

@ mgilson die Antwort ist richtig. Aber Sie sollten sich auch über die oberflächliche Kopie bewusst sein.

def print_arg(**kwargs): 
    print kwargs.pop('key') 
    kwargs['list'].pop() # will affect the original 
    kwargs['dict'].pop('a') #will affect the original 

mydict = {'key':'value', 'list':[2,3,4,5] ,'dict': { 'a':1,'b':2 }} 
print_arg(**mydict) 
print (mydict) # Output : {'dict': {'b': 2}, 'list': [2, 3, 4], 'key': 'value'} 

http://codepad.org/diz38tWF