Tengo una lista que contiene objetos arbitrarios de tipo uniforme:

items = ['a', 'b', 'c', 'x', 'y', 'z']

Estoy escaneando la lista y marcando los objetos colocándolos en un contenedor en función de alguna condición sin importancia. Digamos que son índices extraños:

for i in range(len(items)):
    if i % 2:
        items[i] = (items[i],)

Una segunda pasada filtrará la lista para desenvolver los elementos marcados y eliminar todo lo demás:

items = [x[0] for x in items if isinstance(x, tuple)]

Este código es fundamentalmente funcional. Sin embargo, para matrices muy grandes, el marcado aumenta el uso de memoria y, naturalmente, toma tiempo.

¿Cuál es el contenedor más eficiente para usar para algo como esto? Estoy usando tupla porque tiene la huella más pequeña de todas las clases de contenedores que miré. ¿Hay una mejor manera de envolver una sola referencia?

Esta pregunta surgió como resultado de Corroborando 2 diccionarios Python grandes.

2
Mad Physicist 11 abr. 2020 a las 06:29

2 respuestas

La mejor respuesta

El uso de objetos de contenedor para esto es inherentemente ineficiente en memoria, ya que cada contenedor tomará al menos 40 bytes en un sistema de 64 bits, 8 bytes cada uno para

  • el puntero tipo,
  • el recuento,
  • el puntero de contenido, y
  • Se necesitan dos punteros para el sistema GC de CPython.

Este mínimo de 40 bytes se puede lograr con un contenedor personalizado:

class Wrapper(object):
    __slots__ = ('content',)
    def __init__(self, content):
        self.content = content

O con types.CellType en Python 3.8+:

import types
wrapper = types.CellType(wrapped)
extracted_content = wrapper.cell_contents

O con un medio menos directo de crear celdas de cierre en las versiones de Python inferiores a 3.8:

def make_wrapper(x):
    return (lambda: x).__closure__[0]

wrapper = make_wrapper(wrapped)
extracted_content = wrapper.cell_contents

Pero las técnicas que no involucran un contenedor serán capaces de lograr una sobrecarga de memoria mucho menor.

1
user2357112 supports Monica 11 abr. 2020 a las 04:24

Recomiendo una lista booleana o tupla para el marcado. Si es necesario, puede comprimir esto en un mapa de bits.

flag = [i%2 for i in range(len(items))]

En la segunda pasada, extraiga los elementos necesarios de items:

new_items = [x for x, wanted in zip(items, flag) if wanted]

¿Eso te moverá?

2
Mad Physicist 11 abr. 2020 a las 04:18