Escribir código pitónico más corto y legible

Estoy tratando de producir Python más corto, más pitónico y legible. Y tengo esta solución de trabajo para Problema del proyecto Euler 8 (encontrar el mayor producto de 5 dígitos consecutivos en un número de 1000 dígitos).

¿Sugerencias para escribir una versión más pitónica de este script?

numstring = ''
for line in open('8.txt'):
    numstring += line.rstrip()

nums = [int(x) for x in numstring]

best=0
for i in range(len(nums)-4):
    subset = nums[i:i+5]
    product=1
    for x in subset:
        product *= x
    if product>best:
        best=product
        bestsubset=subset

print best
print bestsubset

Por ejemplo: tiene que haber una sola línea para el siguiente fragmento. Estoy seguro de que hay un tema anterior aquí, pero no estoy seguro de cómo describir lo que estoy haciendo a continuación.

numstring = ''
for line in open('8.txt'):
    numstring += line.rstrip()

¿Alguna sugerencia? ¡gracias chicos!

preguntado el 27 de julio de 12 a las 18:07

5 Respuestas

Estoy trabajando en una respuesta completa, pero por ahora aquí está el trazador de líneas

numstring = ''.join(x.rstrip() for x in open('8.txt'))

Edit: ¡Aquí tienes! Una línea para la búsqueda. Las listas de comprensión son maravillosas.

from operator import mul
def prod(list):
    return reduce(mul, list)

numstring = ''.join(x.rstrip() for x in open('8.txt'))
nums = [int(x) for x in numstring]
print max(prod(nums[i:i+5]) for i in range(len(nums)-4))

Respondido 27 Jul 12, 19:07

Eso es muy ingenioso. ¿Qué piensas de usar una lambda en lugar de mul? es decir def prod(list): return reduce(lambda x,y: x*y, list) - dyln

Eso también funciona bien. No sé por qué Python no lo incorporó; es un requisito bastante común (¡incluso más para el Proyecto Euler!), Y realmente ayudó tenerlo integrado en R. - Rob Volgman

Donde Python ofrece un integrado como operator.mul, generalmente es más eficiente usar eso en lugar de un lambda. Para algo como esto, la eficiencia realmente no importa; tu computadora encontrará la respuesta en un abrir y cerrar de ojos, así que puedes usar lo que prefieras. Pero en general, no está mal tener la costumbre de importar de operator cuando haces cosas con reduce() or map() o lo que sea. - steveha

from operator import mul

def product(nums):
    return reduce(mul, nums)

nums = [int(c) for c in open('8.txt').read() if c.isdigit()]
result = max((product(nums[i:i+5]) for i in range(len(nums))))

Respondido 27 Jul 12, 21:07

@thebjorn: intencionalmente no resté 4 porque no afectó el resultado. Si tuviera que restar, probablemente habría hecho algo como range(len(nums) - 5 + 1) y tal vez incluso nombrar el número mágico en ese punto. - Steven Rumbalski

Aquí se utilizan algunos trucos bastante elegantes. Pero tu uso de max() con un key significa que tu result se establece en la secuencia de 5 números, no en su producto. Sería mejor simplemente usar result = max(product(nums[i:i+5]) for i in range(len(nums))) - steveha

Tengo que decir que me gusta mucho la lista de comprensión que crea nums. No importa usar .replace() para deshacerse de los finales de línea; simplemente extraiga solo los dígitos y conviértalos en números enteros de una sola vez. Elegante. - steveha

@steveha. Leí mal el problema. Pensé que necesitaba la secuencia real. Editaré la respuesta. - Steven Rumbalski

@steveha. ah Ya veo. El OP mantiene la secuencia real, pero el problema del Proyecto Euler no lo requiere. - Steven Rumbalski

Aquí está mi solución. Traté de escribir el código más "Pythonic" que sé escribir.

with open('8.txt') as f:
    numstring = f.read().replace('\n', '')

nums = [int(x) for x in numstring]

def sub_lists(lst, length):
    for i in range(len(lst) - (length - 1)):
        yield lst[i:i+length]

def prod(lst):
    p = 1
    for x in lst:
        p *= x
    return p

best = max(prod(lst) for lst in sub_lists(nums, 5))
print(best)

Podría decirse que este es uno de los casos ideales para usar reduce así que tal vez prod() debiera ser:

# from functools import reduce   # uncomment this line for Python 3.x
from operator import mul
def prod(lst):
    return reduce(mul, lst, 1)

No me gusta tratar de escribir frases ingeniosas donde hay una razón para tener más de una línea. me gusta mucho el with declaración, y es mi hábito usar eso para todas las E/S. Para este pequeño problema, puede hacer una sola línea, y si está usando PyPy o algo así, el archivo se cerrará cuando su pequeño programa termine de ejecutarse y salga. Pero me gusta el uso de dos líneas with así que escribí eso.

Me encanta la frase de @Steven Rumbalski:

nums = [int(c) for c in open('8.txt').read() if c.isdigit()]

Así es como probablemente escribiría eso:

with open("8.txt") as f:
    nums = [int(ch) for ch in f.read() if ch.isdigit()]

Nuevamente, para este tipo de programa corto, su archivo se cerrará cuando el programa finalice, por lo que realmente no necesita preocuparse por asegurarse de que el archivo se cierre; pero me gusta hacer un hábito de usar with.

Respondido 27 Jul 12, 20:07

Sí, creo que la definición de sub_lists(lst, length) Tiene mucho sentido. Fue confuso usar el número mágico como en len(nums)-4. - dyln

Usar una definición de prod como esa es significativamente más lento que usar el incorporado mul de operator. - Rob Volgman

En cuanto a explicar qué fue lo último, primero creas un vacío string , que son numstring:

numstring = ''

Luego recorre cada línea de texto (o línea de strings) en el txt presentar 8.txt:

for line in open('8.txt'):

Entonces, por cada línea que encuentre, desea agregar el resultado de line.rstrip() a la misma. rstrip 'quita' los espacios en blanco (líneas nuevas, espacios, etc.) de la cadena:

    numstring += line.rstrip()

Digamos que tenías un archivo, 8.txt que contiene el texto: LineOne \nLyneDeux\t\nLionTree obtendrías un resultado que se parecería a esto al final:

>>>'LineOne' #loop first time
>>>'LineOneLyneDeux' # second time around the bush
>>>'LineOneLyneDeuxLionTree' #final answer, reggie

Respondido 27 Jul 12, 19:07

Gracias por la cuidadosa explicación @TankorSmash. Debería haber sido más claro en mi pregunta, lo que quise decir fue: No sé cómo describir lo que estoy haciendo aquí de manera lo suficientemente sucinta como para buscar temas anteriores. - dyln

¡Aquí hay una solución completa! Primero lea el número:

with open("8.txt") as infile:
    number = infile.replace("\n", "")

Luego crea una lista de listas con 5 números consecutivos:

cons_numbers = [list(map(int, number[i:i+5])) for i in range(len(number) - 4)]

Luego encuentra el más grande e imprímelo:

print(max(reduce(operator.mul, nums) for nums in cons_numbers))

Si está utilizando Python 3.x, debe reemplazar reduce con functools.reduce.

Respondido 27 Jul 12, 20:07

solo puedes reemplazar '\n' con '' - bernardo

@JBernardo: claro, pero eso se dividirá en cualquier espacio en blanco, y "\n" deja más clara la intención. - orlp

@JBernardo: ah, ahora veo, sí, probablemente sea mejor. - orlp

@ nightcracker: range(len(number) - 5) es un error Pruébelo en '123456789'. le falta el digito 9. - Steven Rumbalski

map, reduce y lambda no se consideran Pythonic por Guido ( artima.com/weblogs/viewpost.jsp?thread=98196 ). - thebjorn

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.