De vez en cuando, me gusta revisar los fundamentos de Python para ver si puedo aprender algo nuevo sobre el lenguaje. En esta ocasión pensé que sería divertido ver algunas formas diferentes de incrementar un número en Python.
Resulta que existen dos formas sencillas de incrementar un número en Python. En primer lugar, se podría utilizar la asignación directa: . Como alternativa, se podría utilizar la sintaxis operador de incremento condensada: . Además, hay algunas opciones menos convencionales como usar el método del módulo o usar expresiones generadoras. Siéntase libre de profundizar a continuación para obtener más información.
Descripción del problema
Al resolver problemas de programación, una operación muy común es agregar un valor fijo a un número. A esta operación la llamamos incremento y es útil en muchos contextos. Por ejemplo, es posible que queramos utilizar un número como contador, para poder realizar un número fijo de operaciones. En ese caso, probablemente comenzaríamos desde cero y agregaríamos uno hasta que se cumpla nuestra condición (por ejemplo ).
Por supuesto, la forma de lograr un incremento varía según el idioma. Por ejemplo, en los lenguajes de estilo C, a menudo hay operadores de incremento directo:
1 2 3 | ++i i++ i += 1 |
Desafortunadamente, algunas de estas opciones anteriores simplemente no funcionan en Python. Por ejemplo, tanto los operadores de preincremento (es decir ) como los de post-incremento (es decir ) fallan en Python:
1 2 3 4 5 | >>> i = 7 >>> i++ SyntaxError: invalid syntax >>> ++i 7 |
Con el operador de incremento posterior, vemos que obtenemos un error de sintaxis evidente. En otras palabras, no es válido. Mientras tanto, el operador de incremento previo se ejecuta, pero en realidad no sucede nada. Eso es porque el operador unario más en Python no hace nada por los números. De hecho, podríamos poner tantas ventajas como queramos:
1 2 | >>> +++++++++++++++++++i 7 |
¡Naturalmente, tendremos que buscar en otra parte si queremos incrementar un número!
Soluciones
Afortunadamente, hay algunas formas de incrementar un valor en Python. De lo contrario, ¿por qué existiría este artículo? En cualquier caso, ¡profundicemos!
Incrementar un número con asignación
Una de las cosas buenas de los números en Python es que son inmutables, lo que significa que no se pueden cambiar. De lo contrario, tendríamos que lidiar con problemas molestos como el alias. Si está interesado en aprender sobre los efectos del aliasing, tengo otro artículo que habla sobre los riesgos de copiar tipos de datos mutables .
En cualquier caso, dado que los números de Python son inmutables, podemos usarlos en aritmética y asignar su valor con facilidad:
1 2 | x = 5 x = x + 1 |
Aquí, hemos definido una variable, que almacena el valor 5. En la siguiente línea, tomamos y le sumamos 1. Luego, almacenamos el resultado nuevamente en . Como resultado, las tiendas 6.
Como alguien que enseña muchas clases de introducción a la programación, encuentro que a los estudiantes a menudo les molesta esta sintaxis la primera vez. Después de todo, la mayoría de los estudiantes están familiarizados con las matemáticas, por lo que no han hecho la conexión que realmente es el operador de asignación, lo que hace que la declaración sea muy legal.
Si esta sintaxis le molesta, mi consejo es que ignore el lado izquierdo (es decir ). En cambio, concéntrese en lo que está sucediendo en el lado derecho de la declaración (es decir ). Esta parte de la declaración se llama expresión y podemos tener literalmente cualquier cosa allí siempre que se evalúe con algún valor. En este caso, podemos evaluar la expresión directamente en tres pasos:
- evalúa a 5
- evalúa a 1
- evalúa a 6
En este punto, el resultado se almacena nuevamente en el que sobrescribe su valor anterior, 5.
Si este desglose de declaraciones y expresiones le parece interesante, le recomiendo que consulte mi artículo que profundiza un poco más en este tema . De lo contrario, veremos la siguiente solución.
Incrementar un número usando un operador
Como la mayoría de los lenguajes de programación, Python tiene una forma de incluir azúcar sintáctico para escenarios como incremento. Dicho esto, sólo hay un cierto operador de incremento: . Para usarlo, necesitaremos reelaborar nuestro código de antes:
1 2 | x = 5 x += 1 |
Como probablemente podamos imaginar, esta declaración funciona exactamente como la línea de la sección anterior. Sin embargo, hemos eliminado algunos códigos redundantes (es decir, los adicionales ).
Una de las cosas buenas de este operador es que crea una declaración independiente. En otras palabras, no se puede incrustar en otros contextos:
1 2 | >>> y = x += 1 SyntaxError: invalid syntax |
Compare esto con los operadores de incremento típicos en otros lenguajes como Java donde esto es posible:
1 | x = x++ |
¿Alguna idea de lo que hace esto? Respuesta: absolutamente nada. En este ejemplo, se incrementa. Luego, se devuelve su valor anterior y se sobrescribe el resultado. En otras palabras, permanece igual. Si eso suena loco, escribí un artículo completo sobre el comportamiento . Es una de las razones por las que me alegro de que la sintaxis nunca haya llegado a Python.
Incrementar un número usando una función
Una cosa que encuentro interesante sobre Python es la gran cantidad de características de lenguaje funcional que tiene. Por ejemplo, además de todos los operadores explícitos, Python incluye un conjunto de sobrecargas funcionales. Como resultado, podríamos incrementar un número sin usar un operador aritmético:
1 2 3 | import operator x = 5 x = operator.add(x, 1 ) |
La ventaja de utilizar una función sobre el operador directo es la escalabilidad. Por ejemplo, podemos encontrar que queremos incrementar una lista completa de valores. En ese caso, la función hace el truco:
1 | list(map(operator.add, [ 1 , 1 , 1 ], [ 5 , - 4 , 13 ])) |
Por supuesto, podría ser un poco más limpio usar el método subyacente :
1 | list(map( 1 .__add__, [ 5 , - 4 , 13 ])) # the space is important |
Dicho esto, esta solución es probablemente la más ridícula para el caso estándar.
Incrementar un número implícitamente
A veces no tiene sentido incrementar manualmente un número. Después de todo, en la descripción de nuestro problema, hablamos de usar un número como contador en un ciclo. La mayoría de las veces, sin embargo, tratamos de evitar contadores explícitos utilizando iteradores. Por ejemplo, si quisiéramos recorrer los caracteres de una cadena, podríamos escribir lo siguiente:
1 2 3 | my_string = "Bob" for character in my_string: pass # Do Something! |
Observe cómo no tuvimos que incrementar explícitamente un contador. Dado que las cadenas son iterables, nos ocupamos de todo eso.
Por supuesto, a veces todavía queremos contar un poco. Después de todo, es posible que deseemos realizar una acción exactamente 5 veces. En ese caso, podemos usar un rango:
1 2 | for i in range( 5 ): pass # Do Something! |
Asimismo, incluso podríamos hacer nuestro propio contador usando una expresión generadora:
1 | counter = (i for i in range( 5 )) |
Luego, para generar términos en la secuencia, podríamos llamar continuamente :
1 2 3 4 | >>> next(counter) 0 >>> next(counter) 1 |
Todas estas opciones realizan una operación de incremento implícitamente. Dependiendo de sus necesidades, eso podría tener más sentido. Por supuesto, eso depende de ti.
Bono: disminuir un número
Odiaría leer un artículo completo hablando de incrementar números sin mencionar la operación del cumplido: decremento. Sin introducir ninguna sintaxis adicional, podemos reducir un número con facilidad:
1 2 | x = 10 x += - 1 |
Por supuesto, eso es un poco contradictorio. En cambio, a menudo optamos por el operador de decremento:
1 | x -= 1 |
Asimismo, la asignación directa funciona igual de bien:
1 | x = x - 1 |
Además, la solución funcional que hemos mencionado se puede modificar para hacer el trabajo:
1 | x = operator.sub(x, 1 ) |
Por supuesto, como ya hemos mencionado, probablemente sea un poco excesivo hacer algo como esto. En su lugar, apégate al operador de decremento.
Actuación
Como siempre, me gusta echar un vistazo a las distintas soluciones y compararlas en términos de rendimiento. Para hacer eso, necesitaremos poner cada solución en su propia cadena:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 dieciséis 17 18 19 20 21 22 23 | setup = "" " import operator "" " assignment = "" " x = 0 x = x + 1 "" " increment = "" " x = 0 x += 1 "" " function = "" " x = 0 x = operator.add(x, 1 ) "" " generator = "" " x = (i for i in range( 5 )) next(x) "" " |
Luego, para probar estas opciones, necesitaremos ejecutarlas con :
1 2 3 4 5 6 7 8 9 | >>> import timeit >>> min(timeit.repeat(setup=setup, stmt=assignment)) 0.03538969999999608 >>> min(timeit.repeat(setup=setup, stmt=increment)) 0.03586820000001012 >>> min(timeit.repeat(setup=setup, stmt=function)) 0.09383009999999103 >>> min(timeit.repeat(setup=setup, stmt=generator)) 0.6202383999999768 |
Naturalmente, los operadores centrales hacen el trabajo más rápido, pero no me encanta la prueba del generador. Como resultado, decidí reescribirlo para que la cadena de configuración incluya el generador hasta un valor muy grande:
1 2 3 4 5 6 7 8 9 | >>> setup = "" " import operator x = (i for i in range( 100000000 )) "" " >>> generator = "" " next(x) "" " >>> min(timeit.repeat(setup=setup, stmt=generator)) 0.11321939999999131 |
Ahora, eso es un poco más respetable. Por supuesto, me pregunto si incluir en la cadena de configuración también cambiará las pruebas originales:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 dieciséis 17 18 19 | >>> setup = "" " import operator x = 0 "" " >>> assignment = "" " x = x + 1 "" " >>> increment = "" " x += 1 "" " >>> function = "" " x = operator.add(x, 1 ) "" " >>> min(timeit.repeat(setup=setup, stmt=assignment)) 0.05624840000001541 >>> min(timeit.repeat(setup=setup, stmt=increment)) 0.061655099999995855 >>> min(timeit.repeat(setup=setup, stmt=function)) 0.12224320000001399 |
En cualquier caso, parece que la asignación directa o los operadores de incremento son la mejor opción. Para poner esto en contexto, ejecuté todas las pruebas usando Python 3.7.3 en una máquina con Windows 10.
Desafío
Cuando pensaba en un buen desafío, me costaba mucho pensar en una idea. Después de todo, hay muchos contextos diferentes en los que incrementar una variable podría ser útil, pero no es exactamente una habilidad en la que podamos desarrollar.
Como resultado, pensé que podría ser más divertido crear una función de incremento compleja que tenga varias condiciones. Por ejemplo, estas son algunas de las condiciones:
- Si el número actual es impar, sume 1
- Si el número actual es par, sume 3
- Si el número actual es divisible por 5, sume 5
Como una arruga adicional, cada número deberá verificarse para los tres criterios. Por ejemplo, el número 15 es impar y divisible por 5. Como resultado, el siguiente número debe ser 21 (es decir, 15 + 5 + 1). Asimismo, el número 12 solo cumplirá los criterios pares, por lo que el siguiente número será el 15.
Como siempre, compartiré una respuesta en los comentarios. ¡Siéntete libre de compartir el tuyo también!
Un pequeño resumen
¡Y con eso, hemos terminado! Una vez más, aquí están todas las soluciones en un lugar conveniente:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 dieciséis 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | x = 0 # Increment by one with assignment x = x + 1 # Increment by one with the increment operator x += 1 # Increment by one with a function import operator x = operator.add(x, 1 ) # Increment by one implicitly on an iterable my_string = "Bob" for character in my_string: pass # Do Something! # Increment by one implicitly using range for i in range( 5 ): pass # Do Something! # Increment by one implicitly using a generator expression counter = (i for i in range( 5 )) next(counter) # Decrement by one with assignment x = x - 1 # Decrement by one with the decrement operator x -= 1 # Decrement by one with a function x = operator.sub(x, 1 ) |
0 Comentarios
Dejanos tu comentario para seguir mejorando!