¿Cómo puedo recopilar los resultados de un cálculo repetido en una lista, diccionario, etc. (o hacer una copia de una lista con cada elemento modificado)?

Resuelto Karl Knechtel asked hace 1 año • 3 respuestas

Hay una gran cantidad de preguntas y respuestas existentes en Stack Overflow sobre este tema general, pero todas son de mala calidad (generalmente, implícitas en un problema de depuración de un principiante) o fallan de alguna otra manera (generalmente por no ser lo suficientemente generales). Hay al menos dos formas extremadamente comunes de equivocarse con el código ingenuo, y los principiantes se beneficiarían más de un canónico sobre el bucle que de cerrar sus preguntas como errores tipográficos o un canónico sobre lo que implica la impresión. Este es mi intento de poner toda la información relacionada en el mismo lugar.

Supongamos que tengo un código simple que hace un cálculo con un valor xy lo asigna a y:

y = x + 1

# Or it could be in a function:
def calc_y(an_x):
    return an_x + 1

Ahora quiero repetir el cálculo para muchos valores posibles de x. Sé que puedo usar un forbucle si ya tengo una lista (u otra secuencia) de valores para usar:

xs = [1, 3, 5]
for x in xs:
    y = x + 1

O puedo usar un whilebucle si hay alguna otra lógica para calcular la secuencia de xvalores:

def next_collatz(value):
    if value % 2 == 0:
        return value // 2
    else:
        return 3 * value + 1

def collatz_from_19():
    x = 19
    while x != 1:
        x = next_collatz(x)

La pregunta es: ¿ cómo puedo recopilar estos valores y usarlos después del ciclo ? Intenté printingresar el valor dentro del bucle, pero no me da nada útil:

xs = [1, 3, 5]
for x in xs:
    print(x + 1)

Los resultados aparecen en la pantalla, pero no encuentro ninguna forma de usarlos en la siguiente parte del código. Entonces creo que debería intentar almacenar los valores en un contenedor, como una lista o un diccionario. Pero cuando intento eso:

xs = [1, 3, 5]
for x in xs:
    ys = []
    y = x + 1
    ys.append(y)

o

xs = [1, 3, 5]
for x in xs:
    ys = {}
    y = x + 1
    ys[x] = y

Después de cualquiera de estos intentos, yssólo contiene el último resultado.

Karl Knechtel avatar Mar 08 '23 02:03 Karl Knechtel
Aceptado

Enfoques generales

Hay tres formas habituales de abordar el problema: utilizando explícitamente un bucle (normalmente un forbucle, pero whilelos bucles también son posibles); mediante el uso de una lista de comprensión (o comprensión de dictado, comprensión de conjuntos o expresión generadora según corresponda a la necesidad específica en el contexto); o mediante el uso del incorporado map(cuyos resultados se pueden usar para construir una lista, establecer o dictar explícitamente).

Usando un bucle explícito

Cree una lista o diccionario antes del ciclo y agregue cada valor a medida que se calcula:

def make_list_with_inline_code_and_for():
    ys = []
    for x in [1, 3, 5]:
        ys.append(x + 1)
    return ys

def next_collatz(value):
    if value % 2 == 0:
        return value // 2
    else:
        return 3 * value + 1

def make_dict_with_function_and_while():
    x = 19
    ys = {}
    while x != 1:
        y = next_collatz(x)
        ys[x] = y # associate each key with the next number in the Collatz sequence.
        x = y # continue calculating the sequence.
    return ys

En ambos ejemplos aquí, el bucle se colocó en una función para etiquetar el código y hacerlo reutilizable. Estos ejemplos muestran returnel ysvalor para que el código de llamada pueda utilizar el resultado . Pero, por supuesto, lo calculado ystambién podría usarse más adelante en la misma función, y bucles como estos también podrían escribirse fuera de cualquier función.

Utilice un forbucle cuando exista una entrada, donde cada elemento debe procesarse de forma independiente. Utilice un whilebucle para crear elementos de salida hasta que se cumpla alguna condición. Python no admite directamente la ejecución de un bucle un número específico de veces (calculadas de antemano); El modismo habitual es hacer un muñeco rangede la longitud adecuada y utilizar un forbucle con él.

Usar una expresión de comprensión o generadora

Una lista por comprensión proporciona una sintaxis elegante para crear una lista a partir de una secuencia de valores existente. Debería preferirse cuando sea posible, porque significa que el código no tiene que centrarse en los detalles de cómo crear la lista, lo que facilita su lectura. También puede ser más rápido , aunque normalmente esto no importará.

Puede funcionar con una llamada de función u otro cálculo (cualquier expresión en términos de los elementos "fuente") y se ve así:

xs = [1, 3, 5]

ys = [x + 1 for x in xs]
# or
def calc_y(an_x):
    return an_x + 1
ys = [calc_y(x) for x in xs]

Tenga en cuenta que esto no reemplazará un whilebucle; no hay una sintaxis válida que la reemplace foraquí while. En general, las listas por comprensión están pensadas para tomar valores existentes y hacer un cálculo separado en cada uno, no para ningún tipo de lógica que implique "recordar" algo de una iteración a la siguiente (aunque esto se puede solucionar, especialmente en Python 3.8 y más tarde ).

De manera similar, se puede crear un resultado de diccionario usando una comprensión de dictado, siempre que se calculen tanto una clave como un valor en cada iteración. Dependiendo de las necesidades exactas, también pueden ser apropiados conjuntos de comprensiones (producir un set, que no contiene valores duplicados) y expresiones generadoras (producir un resultado evaluado de forma diferida; consulte a continuación sobre las expresiones generadoras y).map

Usandomap

Esto es similar a una lista de comprensión, pero aún más específico. mapes una función incorporada que puede aplicar una función repetidamente a múltiples argumentos diferentes de alguna secuencia de entrada (o múltiples secuencias).

Obtener resultados equivalentes al código anterior es el siguiente:

xs = [1, 3, 5]

def calc_y(an_x):
    return an_x + 1

ys = list(map(calc_y, xs))
# or
ys = list(map(lambda x: x + 1, xs))

Además de requerir una secuencia de entrada (no reemplaza un whilebucle), el cálculo debe realizarse usando una función u otro invocable , como la lambda que se muestra arriba (cualquiera de estas, cuando se pasa a map, se llama "función de orden superior" ).

In Python 3.x, map is a class, and calling it therefore creates an instance of that class - and that instance is a special kind of iterator (not a list) that can't be iterated more than once. (We can get something similar using a generator expression rather than a list comprehension; simply use () instead of [].)

Therefore, the code above explicitly creates a list from the mapped values. In other situations, it might not be necessary to do this (i.e., if it will only be iterated over once). On the other hand, if a set is necessary, the map object can be passed directly to set rather than list in the same way. To produce a dictionary, the map should be set up so that each output element is a (key, value) tuple; then it can be passed to dict, like so:

def dict_from_map_example(letters):
    return dict(map(lambda l: (l, l.upper()), letters))
    # equivalent using a dict comprehension:
    # return {l:l.upper() for l in letters}

Generally, map is limited and uncommon compared to list comprehensions, and list comprehensions should be preferred in most code. However, it does offer some advantages. In particular, it can avoid the need to specify and use an iteration variable: when we write list(map(calc_y, xs)), we don't need to make up an x to name the elements of xs, and we don't have to write code to pass it to calc_y (as in the list comprehension equivalent, [calc_y(x) for x in xs] - note the two xs). Some people find this more elegant.

Karl Knechtel avatar Mar 07 '2023 19:03 Karl Knechtel