Por qué no una de python dict.update() devuelve el objeto?

Estoy tratando de hacer :

award_dict = {
    "url" : "http://facebook.com",
    "imageurl" : "http://farm4.static.flickr.com/3431/3939267074_feb9eb19b1_o.png",
    "count" : 1,
}

def award(name, count, points, desc_string, my_size, parent) :
    if my_size > count :
        a = {
            "name" : name,
            "description" : desc_string % count,
            "points" : points,
            "parent_award" : parent,
        }
        a.update(award_dict)
        return self.add_award(a, siteAlias, alias).award

Pero si sentía realmente incómodo en la función, y me hubiera gustado hacer :

        return self.add_award({
            "name" : name,
            "description" : desc_string % count,
            "points" : points,
            "parent_award" : parent,
        }.update(award_dict), siteAlias, alias).award

¿Por qué no actualización de devolver el objeto de modo que la cadena?

JQuery hace esto para hacer de encadenamiento. ¿Por qué no es aceptable en python?

  • * TL;DR newdict = dict(dict001, **dict002)
  • que no funciona en la comprensión, aunque.
  • Sí, eso es, de hecho, uno válido advertencia a punto de salir.
InformationsquelleAutor Paul Tarjan | 2009-09-21

10 Kommentare

  1. 188

    Python, en su mayoría de la implementación de un pragmáticamente teñida sabor de comandos de consulta de separación: los modificadores de retorno None (con pragmáticamente inducida por excepciones como pop😉 por lo cual no pueden ser confundidos con los descriptores de acceso (y en la misma vena, la asignación no es una expresión, la declaración de expresión de la separación está ahí, y así sucesivamente).

    Eso no significa que no hay un montón de maneras de combinar las cosas cuando usted realmente desea, por ejemplo, dict(a, **award_dict) hace una nueva dict es muy parecida a la que al parecer desea .update devuelto — entonces, ¿por qué no usar ESE si usted realmente siente que es importante?

    Editar: btw, no hay necesidad, en su caso específico, para crear a a lo largo del camino, ya sea:

    dict(name=name, description=desc % count, points=points, parent_award=parent,
         **award_dict)

    crea un único dict con exactamente la misma semántica que su a.update(award_dict) (incluyendo, en el caso de conflictos, el hecho de que las entradas en award_dict reemplazar los que estamos dando de forma explícita; para obtener la semántica, es decir, para explícitos, las entradas de «ganar» este tipo de conflictos, pasar award_dict como el único posicional arg, antes de la palabra clave, y desprovistos de la ** forma– dict(award_dict, name=name, etc, etc).

    • Bueno, que va a crear otro diccionario después de que yo tenía que hacer una. Quería crear un diccionario y, a continuación, añadir un montón de otros valores, y luego le dan a una función.
    • y eso es exactamente lo que estamos haciendo … con dos declaraciones (mucho más legible que el anidado de manera que usted quería) que se «sentía realmente incómodo». La edición de mi respuesta a mostrar cómo evitar la creación de a por completo, por cierto,
    • dict(a, **award_dict) sólo rocas y era exactamente lo que yo quería—gracias por eso! (estaba usando dict(d1.items() + d2.items()) antes)
    • Esto no funciona para mí: aa = {'aa':2}: dict(aa, {'bb':2}) salidas *** TypeError: dict expected at most 1 arguments, got 2
    • Debe ser dict(aa, **{'bb':2})
    • Por supuesto, hay maneras más eficaces, que el trabajo por más de dos dicts así: dict(chain(d1.iteritems(), d2.iteritems()))
    • Solución Original no es robusto. Si award_dict contiene las claves ya se ha especificado, un SyntaxError será lanzado por la repetición de una palabra clave argumento. jamylak la solución dict(itertools.de la cadena(d1.iteritems(), .. d<n>.iteritems())) no sólo funciona en el caso de que los diccionarios han duplicado de llaves, pero también permite la combinación de varios diccionarios con dicts más adelante en la cadena, teniendo prioridad para el valor final.
    • cierto, supongo que en este caso no era un problema, aunque. tenga en cuenta que esto funciona bien cuando no pasas ningún específicos kwargs por ejemplo. dict(d1, **d2) siempre funciona afaik
    • Creo que esto puede ser un CPython cosa. PyPy me da «TypeError: las palabras clave deben ser cadenas»
    • También, si las claves de award_dict no son de cadena, el intérprete lanzar una TypeError
    • Muchas gracias por la respuesta, dict(a, **kwargs) es exactamente lo que yo estaba buscando.
    • dict(old_dict, old_key=new_value) no tirar varios valores de la palabra clave y volver de nuevo dict.
    • es interesante que WL (Wolfram Language) viola comando de consulta de separación, haciendo todo lo funcional, sin embargo, es mucho más programador eficiente de python, como de 5:1 o 10:1 LOC para los datos de los flujos de trabajo.

  2. 33

    Python API, por convención, se distingue entre procedimientos y funciones. Funciones de calcular los nuevos valores de sus parámetros (incluyendo cualquier objeto de destino); los procedimientos de modificación de objetos y no devuelven nada (es decir, regresan a Ninguno). Así que los procedimientos tienen efectos secundarios, las funciones no. la actualización es un procedimiento, por lo que no devuelve un valor.

    La motivación para hacerlo de esa manera es que, de lo contrario, usted puede obtener los efectos secundarios indeseables. Considere la posibilidad de

    bar = foo.reverse()

    Si inverso (lo cual invierte la lista en el lugar) también podría devolver la lista, los usuarios pueden pensar que invertir devuelve una nueva lista en la que se le asigna a la barra, y nunca note que foo también se modificó. Haciendo de retorno inverso de Ninguno, que cuenta inmediatamente de que la barra no es el resultado de la reversión, y se mira más de cerca lo que el efecto de la inversa es.

    • Gracias. ¿Por qué no invertir también dar la opción a no hacerlo directamente? El rendimiento? haciendo reverse(foo) se siente extraño.
    • La adición de una opción sería inapropiado: que iba a cambiar la naturaleza del método en función de un parámetro. Sin embargo, los métodos que realmente debe tener fijos los tipos de devolución (por desgracia, hay casos en los que esta regla se rompe). Es fácil crear un revertido copia: acabo de hacer una copia (utilizando bar=foo[:]), a continuación, volver a la copia.
    • Creo que la razón es lo explícito. En bar = foo.reverse(), usted podría pensar que foo no es modificado. Para evitar confusiones, tiene foo.reverse() y bar = reversed(foo).
    • Lo que está mal con el cambio de la naturaleza de un parámetro en función de un parámetro?
  3. 13
    >>> dict_merge = lambda a,b: a.update(b) or a
    >>> dict_merge({'a':1, 'b':3},{'c':5})
    {'a': 1, 'c': 5, 'b': 3}

    Tenga en cuenta que así como devolver la fusión dict, se modifica el primer parámetro en el lugar. Así dict_merge(a,b) modificar un.

    O, por supuesto, usted puede hacer todo en línea:

    >>> (lambda a,b: a.update(b) or a)({'a':1, 'b':3},{'c':5})
    {'a': 1, 'c': 5, 'b': 3}
    • -1 lambda no debe ser usado como tal, en lugar del uso convencional de la función def lugar
    • Ni siquiera necesita una lambda, sólo tiene que utilizar a.update(b) or a
  4. 8

    no es suficiente reputación para el comentario a la izquierda en la parte superior de respuesta

    @beardc este no parece ser CPython cosa. PyPy me da «TypeError: las palabras clave deben ser cadenas»

    La solución con **kwargs sólo funciona porque el diccionario para combinar sólo ha claves de la cadena de tipo.

    es decir,

    >>> dict({1:2}, **{3:4})
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: keyword arguments must be strings

    vs

    >>> dict({1:2}, **{'3':4})
    {1: 2, '3': 4}
  5. 5

    No es que no es aceptable, sino que dicts no fueron implementadas de esa manera.

    Si usted mira Django del ORM, que hace un amplio uso de encadenamiento. No se desanime, que incluso podría heredar de dict y reemplazar sólo update para hacer la actualización y return self, si usted realmente lo desea.

    class myDict(dict):
        def update(self, *args):
            dict.update(self, *args)
            return self
    • Gracias, esto podría parche dict, yo sólo quería saber por qué dict() no permitir esta funcionalidad en sí (pues es tan fácil como lo demuestran). Hace Django parche dict como este?
  6. 5

    Esta es fácil:

    (lambda d: d.update(dict2) or d)(d1)
    • esta es una gran solución, hombre. muy bonito!
    • ¿por qué usted sólo tiene 31 puntos? ir a hacer un poco más (bueno) respuestas!
  7. 2

    tan cerca de la solución que usted propone como yo podría conseguir

    from collections import ChainMap
    
    return self.add_award(ChainMap(award_dict, {
        "name" : name,
        "description" : desc_string % count,
        "points" : points,
        "parent_award" : parent,
    }), siteAlias, alias).award
  8. 0
    import itertools
    dict_merge = lambda *args: dict(itertools.chain(*[d.iteritems() for d in args]))
  9. 0

    Estado tratando de esto mismo en Python 3.4 (de modo que no era capaz de utilizar la fantasía {**dict_1, **dict_2} sintaxis).

    Yo quería ser capaz de tener una cadena que no son claves en los diccionarios, así como proporcionar una cantidad arbitraria de los diccionarios.

    También, yo quería hacer un diccionario nuevo, así que he optado por no usar collections.ChainMap (un poco la razón por la que no desea utilizar dict.update inicialmente.

    Aquí es lo terminé de escribir:

    def merge_dicts(*dicts):
        all_keys  = set(k for d in dicts for k in d.keys())
        chain_map = ChainMap(*reversed(dicts))
        return {k: chain_map[k] for k in all_keys}
    
    merge_maps({'1': 1}, {'2': 2, '3': 3}, {'1': 4, '3': 5})
    # {'1': 4, '3': 5, '2': 2}
  10. 0

    Para los que llegan tarde a la fiesta, me había puesto algo de tiempo juntos (Py 3.7), mostrando que .update() métodos basados en la mirada un poco (~5%) más rápido cuando las entradas se conservan y notablemente (~30%) más rápido cuando se acaba de actualizar en el lugar.

    Como de costumbre, todos los puntos de referencia debe ser tomado con un grano de sal.

    def join2(dict1, dict2, inplace=False):
        result = dict1 if inplace else dict1.copy()
        result.update(dict2)
        return result
    
    
    def join(*items):
        iter_items = iter(items)
        result = next(iter_items).copy()
        for item in iter_items:
            result.update(item)
        return result
    
    
    def update_or(dict1, dict2):
        return dict1.update(dict2) or dict1
    
    
    d1 = {i: str(i) for i in range(1000000)}
    d2 = {str(i): i for i in range(1000000)}
    
    %timeit join2(d1, d2)
    # 258 ms ± 1.47 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    %timeit join(d1, d2)
    # 262 ms ± 2.97 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    %timeit dict(d1, **d2)
    # 267 ms ± 2.74 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    %timeit {**d1, **d2}
    # 267 ms ± 1.84 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

    Los tiempos para el lugar de operaciones son un poco más complicado, por lo que tendría que ser modificado a lo largo de una copia adicional de la operación (el primer momento es sólo para referencia):

    %timeit dd = d1.copy()
    # 44.9 ms ± 495 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
    
    %timeit dd = d1.copy(); join2(dd, d2)
    # 296 ms ± 2.05 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    %timeit dd = d1.copy(); join2(dd, d2, True)
    # 234 ms ± 1.02 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    %timeit dd = d1.copy(); update_or(dd, d2)
    # 235 ms ± 1.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea