Estaba revisando el código de la función groupby de la biblioteca groupby en Python y encontré esto:
def groupby(key, seq): """ Group a collection by a key function """ if not callable(key): key = getter(key) d = collections.defaultdict(lambda: [].append) for item in seq: d[key(item)](item) rv = {} for k, v in d.items(): rv[k] = v.__self__ return rv ¿Hay alguna razón para usar rv[k] = v.__self__ en lugar de rv[k] = v ?
Este es un truco algo confuso para ahorrar una pequeña cantidad de tiempo:
Estamos creando un defaultdict con una función de fábrica que devuelve un método de adición enlazado de una nueva instancia de lista con [].append append Entonces podemos simplemente hacer d[key(item)](item) en lugar de d[key(item)].append(item) como lo haríamos si creamos un defaultdict que contiene listas. Si no buscamos append cada vez, ganamos una pequeña cantidad de tiempo.
Pero ahora el dict contiene métodos vinculados en lugar de las listas, por lo que tenemos que recuperar la instancia de la lista original a través de __self__ .
__self__ es un atributo descrito para métodos de instancia que devuelve la instancia original. Puedes verificar eso con esto por ejemplo:
>>> a = [] >>> a.append.__self__ is a TrueEste es un enfoque un tanto complicado, pero posiblemente más eficiente para crear y usar un defaultdict de listas.
Primero, recuerde que el elemento predeterminado es lambda: [].append . Esto significa crear una nueva lista y almacenar un método de append enlazado en el diccionario. Esto le ahorra un enlace de método en cada adición adicional a la misma clave, y la recolección de elementos no utilizados que sigue. Por ejemplo, el siguiente enfoque más estándar es menos eficiente:
d = collections.defaultdict(list) for item in seq: d[key(item)].append(item) Entonces, el problema es cómo recuperar las listas originales del diccionario, ya que la referencia no se almacena explícitamente. Afortunadamente, los métodos vinculados tienen un atributo __self__ que hace precisamente eso. Aquí, [].append.__self__ es una referencia al [] original.
Como nota al margen, el último bucle podría ser una comprensión:
return {k: v.__self__ for k, v in d.items()}