python function dictionary scope

python - ¿Es siempre seguro modificar el diccionario `** kwargs`?



function dictionary (3)

Usando la sintaxis de la función Python def f(**kwargs) , en la función se crea un diccionario de palabras clave kwargs , y los diccionarios son mutables, entonces la pregunta es, si kwargs diccionario kwargs , ¿es posible que tenga algún efecto? fuera del alcance de mi función?

Según entiendo cómo funciona el desembalaje de diccionarios y el empaquetado de argumentos de palabras clave, no veo ninguna razón para creer que pueda ser inseguro, y me parece que no hay peligro de esto en Python 3.6:

def f(**kwargs): kwargs[''demo''] = 9 if __name__ == ''__main__'': demo = 4 f(demo=demo) print(demo) # 4 kwargs = {} f(**kwargs) print(kwargs) # {} kwargs[''demo''] = 4 f(**kwargs) print(kwargs) # {''demo'': 4}

Sin embargo, ¿es esta implementación específica, o es parte de la especificación de Python? ¿Estoy pasando por alto cualquier situación o implementación en la que (salvo modificaciones a argumentos que son ellos mismos mutables, como kwargs[''somelist''].append(3) ) este tipo de modificación podría ser un problema?


Ambas respuestas anteriores son correctas al afirmar que técnicamente, los kwargs mutantes nunca tendrán un efecto en los ámbitos principales.

Pero ... ese no es el final de la historia . Es posible que una referencia a kwargs se comparta fuera del alcance de la función, y luego te kwargs con todos los problemas de estado mutados compartidos habituales que podrías esperar.

def create_classes(**kwargs): class Class1: def __init__(self): self.options = kwargs class Class2: def __init__(self): self.options = kwargs return (Class1, Class2) Class1, Class2 = create_classes(a=1, b=2) a = Class1() b = Class2() a.options[''c''] = 3 print(b.options) # {''a'': 1, ''b'': 2, ''c'': 3} # other class''s options are mutated because we forgot to copy kwargs

Técnicamente, esto responde a su pregunta, ya que compartir una referencia a kwargs mutable conduce a efectos fuera del ámbito de la función.

Me han mordido varias veces esto en el código de producción, y es algo que explícitamente cuido por ahora, tanto en mi propio código como cuando reviso otros. El error es obvio en mi ejemplo artificial anterior, pero es mucho más engañoso en el código real cuando se crean funciones de fábrica que comparten algunas opciones comunes.


Para el código de nivel Python, el dict kwargs dentro de una función siempre será un nuevo dict.

Sin embargo, para las extensiones C , ten cuidado. La versión C API de kwargs a veces pasa un dict directamente. En versiones anteriores, incluso pasaría las subclases dict directamente, lo que llevaría al error ( ahora corregido ) donde

''{a}''.format(**collections.defaultdict(int))

produciría ''0'' lugar de generar un KeyError .

Si alguna vez tiene que escribir extensiones C, posiblemente incluyendo Cython, no intente modificar el equivalente kwargs , y kwargs cuidado con las subclases dict en las versiones antiguas de Python.


Siempre es seguro. Como dice la especificación

Si el formulario "** identificador" está presente, se inicializa a una nueva asignación ordenada que recibe cualquier exceso de argumentos de palabra clave, por defecto a una nueva asignación vacía del mismo tipo.

Énfasis añadido.

Siempre se garantiza que obtendrá un nuevo objeto de correlación dentro del invocable. Mira este ejemplo

def f(**kwargs): print((id(kwargs), kwargs)) kwargs = {''foo'': ''bar''} print(id(kwargs)) # 140185018984344 f(**kwargs) # (140185036822856, {''foo'': ''bar''})

Entonces, aunque f puede modificar un objeto que se pasa a través de ** , no puede modificar el objeto ** autor de la llamada.

Actualización : ya que preguntaste sobre casos de esquina, aquí hay un infierno especial para ti que de hecho modifica los kwargs la persona que kwargs :

def f(**kwargs): kwargs[''recursive!''][''recursive!''] = ''Look ma, recursive!'' kwargs = {} kwargs[''recursive!''] = kwargs f(**kwargs) assert kwargs[''recursive!''] == ''Look ma, recursive!''

Sin embargo, es probable que no lo veas en la naturaleza.