2013-04-16 11 views
9

Rozważmy przypadek, w którym można zmienić kwargs dict wewnątrz metody:zmienność z ** kwargs argumentu w Pythonie

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

Gdybym wywołać metodę pop_arg ze słownikiem jak ten:

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

będzie mydict być zmieniony przez to połączenie?

Interesuje mnie również bardziej szczegółowe wyjaśnienie mechanizmu wywoływania metod, który pozwala na zmianę mydict.

Odpowiedz

6

Zobaczmy:

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   

 

Przekonajmy co CALL_FUNCTION_KW robi (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

Nie, mydict nie zostanie zmieniony. Kwargs zostanie rozpakowany do nowego słownika.

Rozważmy przypadek, gdzie trzeba:

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

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

W tym przypadku, to jest oczywiste, że jest to inny kwargs DICT niż zdać się bo gdy robi rozpakowany, że brakuje klucza 'key'.

+3

Warto zauważyć wyraźnie, że będzie to płytkie kopię choć. –

8

@ Odpowiedź mgilsona jest poprawna. Ale powinieneś także wiedzieć o płytkiej kopii.

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

Powiązane problemy