简体   繁体   English

Python中** kwargs参数的可变性

[英]Mutability of the **kwargs argument in Python

Consider a case where I change the kwargs dict inside a method: 考虑一下我在方法内部更改kwargs dict的情况:

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

If I call the method pop_arg with a dictionary like this: 如果我用这样的字典调用方法pop_arg

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

will mydict be changed by this call? 这个电话会改变mydict吗?

I am also interested in a more detailed explanation of the underlying method calling mechanism that lets mydict change or not. 我也对使mydict更改或不更改的基础方法调用机制的更详细说明感兴趣。

No, mydict won't be changed. 不, mydict不会改变。 kwargs get unpacked into a new dictionary. 小矮人被打包成一本新字典。

Consider the case where you have: 考虑以下情况:

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

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

In this case, it's obvious that kwargs is a different dict than you pass in because when it gets unpacked, it's missing the 'key' key. 在这种情况下,很明显,kwargs是与您传入的字典不同的字典,因为当将其打开包装时,它会丢失'key'键。

@mgilson's answer is correct. @mgilson的答案是正确的。 But you should also be aware about the shallow copy. 但您也应该注意浅表副本。

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 http://codepad.org/diz38tWF

Let's see: 让我们来看看:

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        

Let's find what CALL_FUNCTION_KW does ( ceval.c ): 让我们找到CALL_FUNCTION_KWceval.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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM