style standard name code python dictionary coding-style

python - standard - Diccionarios y valores predeterminados



python package name (8)

(esta es una respuesta tardía)

Una alternativa es subclasificar la clase dict e implementar el __missing__() , como este:

class ConnectionDetails(dict): def __missing__(self, key): if key == ''host'': return "localhost" raise KeyError(key)

Ejemplos:

>>> connection_details = ConnectionDetails(port=80) >>> connection_details[''host''] ''localhost'' >>> connection_details[''port''] 80 >>> connection_details[''password''] Traceback (most recent call last): File "python", line 1, in <module> File "python", line 6, in __missing__ KeyError: ''password''

Suponiendo que connectionDetails es un diccionario de Python, ¿cuál es la mejor, más elegante y más "pitonica" forma de refactorizar código como este?

if "host" in connectionDetails: host = connectionDetails["host"] else: host = someDefaultValue


Hay un método en los diccionarios de python para hacer esto: dict.setdefault

connectionDetails.setdefault(''host'',someDefaultValue) host = connectionDetails[''host'']

Sin embargo, este método establece el valor de connectionDetails[''host''] en someDefaultValue si el host clave no está ya definido, a diferencia de lo que hizo la pregunta.


Me gusta esto:

host = connectionDetails.get(''host'',''someDefault'')


Mientras que .get() es un buen modismo, es más lento que if/else (y más lento que try/except si se puede esperar la presencia de la clave en el diccionario la mayor parte del tiempo):

>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}", ... stmt="try:/n a=d[1]/nexcept KeyError:/n a=10") 0.07691968797894333 >>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}", ... stmt="try:/n a=d[2]/nexcept KeyError:/n a=10") 0.4583777282275605 >>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}", ... stmt="a=d.get(1, 10)") 0.17784020746671558 >>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}", ... stmt="a=d.get(2, 10)") 0.17952161730158878 >>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}", ... stmt="if 1 in d:/n a=d[1]/nelse:/n a=10") 0.10071221458065338 >>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}", ... stmt="if 2 in d:/n a=d[2]/nelse:/n a=10") 0.06966537335119938


Para múltiples valores predeterminados diferentes pruebe esto:

connectionDetails = { "host": "www.example.com" } defaults = { "host": "127.0.0.1", "port": 8080 } completeDetails = {} completeDetails.update(defaults) completeDetails.update(connectionDetails) completeDetails["host"] # ==> "www.example.com" completeDetails["port"] # ==> 8080


Probando la sospecha de @Tim Pietzcker sobre la situación en PyPy (5.2.0-alpha0) para Python 3.3.5, encuentro que, de hecho, tanto .get() como el modo if / else funcionan de manera similar. En realidad, parece que en el caso if / else solo hay una sola búsqueda si la condición y la asignación involucran la misma clave (compárelo con el último caso donde hay dos búsquedas).

>>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}", .... stmt="try:/n a=d[1]/nexcept KeyError:/n a=10") 0.011889292989508249 >>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}", .... stmt="try:/n a=d[2]/nexcept KeyError:/n a=10") 0.07310474599944428 >>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}", .... stmt="a=d.get(1, 10)") 0.010391917996457778 >>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}", .... stmt="a=d.get(2, 10)") 0.009348208011942916 >>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}", .... stmt="if 1 in d:/n a=d[1]/nelse:/n a=10") 0.011475925013655797 >>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}", .... stmt="if 2 in d:/n a=d[2]/nelse:/n a=10") 0.009605801998986863 >>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}", .... stmt="if 2 in d:/n a=d[2]/nelse:/n a=d[1]") 0.017342638995614834


Puede usar una función lamba para esto como una línea. Haga un nuevo objeto connectionDetails2 que se accede como una función ...

connectionDetails2 = lambda k: connectionDetails[k] if k in connectionDetails.keys() else "DEFAULT"

Ahora usa

connectionDetails2(k)

en lugar de

connectionDetails[k]

que devuelve el valor del diccionario si k está en las teclas, de lo contrario, devuelve "DEFAULT"


También puedes usar el defaultdict así:

from collections import defaultdict a = defaultdict(lambda: "default", key="some_value") a["blabla"] => "default" a["key"] => "some_value"

Puede pasar cualquier función ordinaria en lugar de lambda:

from collections import defaultdict def a(): return 4 b = defaultdict(a, key="some_value") b[''absent''] => 4 b[''key''] => "some_value"