Underc0de

Programación Scripting => Python => Mensaje iniciado por: Mortal_Poison en Noviembre 04, 2017, 09:35:00 PM

Título: [PARTE 1] ¡Aprende Python Avanzado!
Publicado por: Mortal_Poison en Noviembre 04, 2017, 09:35:00 PM

Hola a tod@s,

Hoy les traigo un aporte de Python Avanzado,donde asumo que ya conocen y tienen buenas bases de Python. Estos tutoriales también los llevaré a mi canal donde los explicaré con voz, por si a alguien le interesa. Además de eso, también mostraré las vulnerabilidades que se cometen cuando se programan en éstos lenguajes de Scripting, ya sea PHP, Python(con sus frameworks) y entre otros(será en otro hilo).

Comencemos...

(https://sdtimes.com/wp-content/uploads/2014/08/0815.sdt-python.jpg)
Ilustración 1. Guido van Rossum

No sé ustedes, pero yo vi esa imagen y se me vino a la mente Kevin Mitnick(por su parecido físico xd).

Por la década de los 80's, cuando se creo éste lenguaje de scripting que muchos amamos, comenzó a evolucionar con su misma filosofía: proporcionar un lenguaje de programación que fuera multiparadigma con legibilidad y productividad. La cuestión es, la gente solía ver a Python como un lenguaje de Scripting y no como lo anterior. Sin embargo, las empresas pioneras en ese entonces, notaron que con Python podían construir cualquier tipo de sistema. Y así fue como se convirtió en lo que hoy conocemos: en un lenguaje de Scripting.

Cabe aclarar que abarcaré todo desde la última versión de Python, la 3.x.x.

Python 3, a pesar de sus nuevas y emocionantes características, todavía no es adoptado en la comunidad. Notemos en Python 3 WoS(Wall Of SuperPowers), (https://python3wos.appspot.com/) donde observamos la compatibilidad de los paquetes más populares en Python 3, fue llamada Python 3 Wall Of Shame. Es un poco triste, pero esto pasa con cualquier versión de cualquier lenguaje de programación: Java, por qué lo "detestan". (https://underc0de.org/foro/debates/t34638/)

Esta situación está cambiando poco a poco, de hecho, si notan, en la página los paquetes enumerados se están poniendo de color verde, precisamente porque se está avanzando en este aspecto.

La gente suele comentar: "los paquetes de Python 2 a la versión de Python 3 no se encuentran". Pues cuando la página esté totalmente verde como un trébol, ésta gente no tendrá excusa para aprovechar el máximo potencial de la versión de Python 3. A pesar de todo, tienen razón. He desarrollado herramientas(que son privadas pero que he presentado en conferencias) que están en Python 2.x por su compatibilidad. El problema radica en que, permutar una aplicación existente en Python 2.x a Python 3.x resulta un completo problema. Pero como existen páginas para convertir migraciones de Oracle a Mysql, también las hay en Python. Claro está, esto no garantiza un 100% de que todo funcionará correctamente(porque lo he comprobado) sino más bien de migrarlo y verificar todo de nuevo. La herramienta para transformar el código de Python 2.x a Python 3.x se llama 2to3. (https://docs.python.org/2/library/2to3.html)

(https://pbs.twimg.com/profile_images/450255086388666369/2fK00oB-.jpeg)
Ilustración 2. Mongomering Berns aludiendo a las empresas.

Ahora imaginen, si a mi me cuesta un poco, a las organizaciones un proceso como esos les va a costar el doble o incluso el triple, y eso ellas no lo pueden permitir.

(http://www.robmv.com/images/architect.jpg)
Ilustración 3. Arquitecto de Software.

Sin embargo, realizar un cambio de estos lo podemos hacer mediante metodologías de diseños de arquitectura de software, como por ejemplo la orientada a servicios (https://blog.powerdata.es/el-valor-de-la-gestion-de-datos/bid/394442/qu-es-la-arquitectura-orientada-a-servicios-soa) o microservicios (https://cjrequena.github.io/micro-services/2016/09/20/micro-services-architecture-es.html), donde se puede conseguir un objetivo bastante grande y con muy buenos resultados. No me centraré en cuáles diseños de Arquitectura de Software existen, de esto, existen estudios hasta posdoctorados.

Sin embargo, yo recomendaría a las empresas hacerlo, los beneficios que trae Python 3 es verdaderamente fabuloso. Basémonos en el PEP-404, (https://www.python.org/dev/peps/pep-0404/) éste es el que nos indica qué va a ocurrir con Python 2 y por qué migrar a Python 3. Se comenta en el artículo que ya no habrá versión 2.8 en la versión de Python y que además, en un momento determinado a posteriori, proyectos principales como Django, Numpy y Flask eliminarán la compatibilidad con la versión 2.x y solo estarán disponibles en la versión 3. Es un aspecto muy importante que quería tocar, por el hecho que muchos de los desarrolladores se abstienen a migrar a una versión de Python 3.

Principales diferencias entre Python 2 y Python 3

Mostraré las principales diferencias que existen con ejemplos, recordando a un amigo que una vez me dijo: "ese código no sirve, da error por todo". Lo que no se dió cuenta, era que estaba en Python 3 y no en Python 2.

Para los que no estén familiarizados, por favor remitirse al siguiente enlace. (https://docs.python.org/3.0/whatsnew/3.0.html)


Comencemos por los cambios de sintaxis, en Python 2 imprimir un mensaje sería lo siguiente:
Código (python) [Seleccionar]
print "El mundo caerá en caos"

Mientras que en Python 3:
Código (python) [Seleccionar]
print("El mundo caerá en caos")

Obviamente, si intentamos ejecutar con Python 2 el código de Python 3, nos generará error y viceversa.

(https://i.imgur.com/aj6qcPE.png)
Ilustración 4. Ejecutando con Python 3 un mensaje con la sintaxis de Python 2.

Esa es una de las diferencias, además de:
print ya no es una declaración sino una función, esa es la razón por lo que el paréntesis ahora es obligatorio.
➡ Las excepciones cambiaron: from except exc, var a except, exc como var.
➡ El operador de comparación <> ha sido removido y cambiado a !=.
➡ La función sort() y las listas con método sorted() ya no aceptan el argumento cmp argument. (http://portingguide.readthedocs.io/en/latest/comparisons.html#the-cmp-argument)
➡ Las expresiones de divisiones entre enteros como por ejemplo 3/2, retornará flotantes. Para ello debemos usar el operador //. Esto es muy bueno, porque podemos hacer una división como: 10//7 y el resultado será 1.

Compatibilidades entre versiones

Realmente se torna complicado cuando queremos hacer esto. Sin embargo, vale la pena. La mejor manera hasta ahora para definir cómo puede cambiar la compatibilidad en el futuro es mediante SemVer. (http://semver.org/)

SemVer describe un estándar que es ampliamente aceptado a nivel mundial para marcar el alcance del cambio en el código mediante el especificador de la versión que consiste en solo tres números. También ofrece algunos consejos de cómo manejar las políticas para desaprobar esto. Podemos obtener más detalles en su página oficial, donde nos indica el formato MAYOR, MENOR o PATCH dependiendo el número de versión.

La compatibilidad muchas veces la podemos agregar en un fichero llamado compat.py, de hecho, hay mucha información acerca de esto:
En GitHub un proyecto. (https://github.com/swistakm/python-gmaps/tree/master/src/gmaps)
Información de compatibilidad. (https://pythonhosted.org/WebCore/core/compat.html)

Si vemos el código de compat.py en el proyecto de GitHub, nos damos cuenta que es de la siguiente forma:

Código (python) [Seleccionar]
# -*- coding: utf-8 -*-
import sys
## Condición para verificar la versión de Python.
if sys.version_info < (3, 0, 0):
    import urlparse  # Si es menor a la 3.0

    def is_string(s):
        return isinstance(s, basestring)  # noqa
## Sino, usamos la librería de Python 3.
else:
    from urllib import parse as urlparse  # noqa

    def is_string(s):
        return isinstance(s, str)


Esa es una forma muy conveniente para las dependencias de librerías y asumir la compatiblidad en nuestros proyectos de Python.

Hablemos de Jython

(http://www.jython.org/css/jython.png)
Ilustración 5. Logo de Jython.

Nos permite usar sin problemas las clases de Java dentro de nuestros módulos de Python. Jython, (http://www.jython.org/)  utilizar sin problemas las clases de Java dentro de sus módulos de Python. Tenemos que Jython es multiplataforma, posee velocidad y memoria y además, también tiene sincronismo multihilo. Pylons, (https://pylonsproject.org/) uno de los frameworks también está intentando promover e incentivar para que Jython se encuentre en el mundo de Java.

Hablemos de IronPython

(http://4.bp.blogspot.com/-m3fZAYlDlXg/TcHbAAVDiwI/AAAAAAAADo4/U90ki9oNj0s/s1600/ironpython-logo.png)
Ilustración 6. Logo de IronPython.

IronPython (http://ironpython.net) nos permite integrar Python al Framework de .NET. El proyecto es apoyado por Microsoft, donde trabajan los desarrolladores líderes de IronPython. Es una implementación bastante importante para la promoción de un idioma. Además de Java que posee una de las comunidades desarrolladoras más grandes que existen, .NET no es la excepción. También creo que vale la pena resaltar que Microsoft proporciona un conjunto de herramientas de desarrollo gratuitas que convierten a Visual Studio en un IDE de Python completo. Esto se logra con plugins desarrollados como PVTS(Python Tools for Visual Studio) de Visual Studio y está disponible como código fuente abierto. Lo puedes descargar del siguiente enlace. (http://microsoft.github.io/PTVS)


Hablemos de PyPy

(https://pypy.org/image/pypy-logo.png)
Ilustración 7. Logo de PyPy.

PyPy (https://pypy.org/) es probablemente una de las implementación más emocionantes, ya que su objetivo es volver a escribir código Python en Python. En PyPy, el intérprete de Python está escrito en Python. Tenemos una capa de código C que lleva a cabo el trabajo de nuts and bolts para la implementación CPython de Python. Sin embargo, en la implementación de PyPy, esta capa de código C se reescribe totalmente en Python puro. ¿Qué significa esto? significa que puede cambiar el comportamiento del intérprete durante el tiempo de ejecución e implementar patrones de código que no podrían hacerse fácilmente en CPython. En la actualidad, PyPy apunta a ser totalmente compatible con la versión de Python 2.7, mientras que PyPy3 es compatible con la versión de Python 3.2.5.

Command Shells para Python

Existen varios, sabemos que python al instalarlo trae un command line, sin embargo, aquí van otros.

bpython

bpython posee una interfaz elegante para el intérprete de Python. Éstos son algunos de las cosas que podemos obtener con bpython:
• Resaltado de sintaxis en línea
• Autocompletar similar a Readline con sugerencias que se muestran a medida que escribe
• Autoindentación(para las personas que dicen que odian python por su indentación  ::) )
• Soporte de Python 3
• Listas de parámetros esperados para cualquier función de Python

Descargar del siguiente enlace bpython. (http://bpython-interpreter.org/)


iPython

IPyhton (http://ipython.scipy.org) proporciona un shell de comandos de Python extendido. Entre sus características, enumeraré las que son más interesantes:
• Introspección dinámica de objetos
• Acceso al shell del sistema desde el prompt
• Soporte directo de creación de perfiles
• Instalaciones de depuración

Ahora, IPython es una parte del proyecto más grande conocido como Jupyter (http://jupyter.org/) que proporciona notebooks interactivos con código en vivo que puede ser escrito en muchos idiomas distintos.

Descargar del siguiente enlace IPyhton. (https://ipython.org/)

ptpython

ptpython es un proyecto open source, la implementación de las utilidades básicas del núcleo está disponible como un paquete separado llamado prompt_toolkit. Esto permite crear varias interfaces interactivas de línea de comandos que estéticamente son agradables. A menudo se suele comparar con bpython en el aspecto de funcionaldiades, pero la principal diferencia es que habilita un modo de compatibilidad con IPython y su sintaxis habilita características adicionales como %pdb,%cpaste o %perfil.

Descargar del siguiente enlace ptpython. (https://github.com/jonathanslenders/ptpython/)

Para finalizar les comentaré de varios debuggers que uso cuando me estoy dando cabezasos contra la pared al no encontrar por qué me sale error :-\.

Debugging In Python

La depuración de código es un elemento esencial en el proceso de desarrollo de software. Muchos programadores/desarrolladores pueden pasar la mayor parte de su vida utilizando únicamente registros extensos como sus principales herramientas de depuración, pero la mayoría de los desarrolladores profesionales prefieren confiar en algún tipo de depurador para sus tareas diarias. Python, ya viene con un depurador interactivo incorporado llamado pdb, para los que no lo conocían, pueden consultar en el siguiente enlace (//http://https:%20/%20/docs.python.org/3/library/pdb.html)más información. Se puede invocar desde la línea de comandos en el script existente, por lo que Python ingresará a la depuración post-mortem si el programa sale anormalmente(muy común xd):

Código (python) [Seleccionar]
python -m pdb Mortal_Poison.py

Éste tipo de depuración post-mortem si bien es útil, no abarca todos los escenarios. Pragmáticamente solo es útil cuando la aplicación existente posee alguna excepción si se produce el error. En muchos casos, el código defectuoso se comporta de forma anormal pero no se cierra inesperadamente(no sé si se acuerden de lo de los temas de errores en tiempo de ejecución / compilación). En esos casos, podemos indicarle a el debugger que se establezca en una línea de código específica, para ello usamos:

Código (python) [Seleccionar]
import pdb; pdb.set_trace()

Esto hará que el intérprete de Python inicie la sesión del depurador en esa línea de cñidugi, Es muy útil cuando tenemos problemas de localizar y a primera vista, también nos es muy familair con el depurador de GNU(GDB). Como sabemos, Python es un lenguaje dinámico y la sesión de pdb es muy similar a una sesión de intérprete ordinaria. Es decir, el desarrollador no solo se limita a localizar el código debuggeado sino que también puede realizar importaciones a módulos o también, invocar cualquier código.

Lamentablemente, la primera experiencia con pdb puede ser un poco abrumadora y frustante, debido a la existencia de comandos crípticos de depuración de letras cortas como h, b, s, n, j y r. En caso de duda, el comando help pdb escrito durante la sesión del depurador proporcionará un uso extenso e información adicional que nos proporcionará ayuda. La sesión del depurador en pdb también es muy simple y no proporciona funciones adicionales, como la finalización de pestañas o el resaltado del código. Afortunadamente, hay pocos paquetes disponibles en PyPI que proporcionen tales características disponibles a partir de shells de Python alternativos mencionados anteriormente.

Los ejemplos más notables son:
ipdb (https://pypi.python.org/pypi/ipdb): Paquete separado basado en iPython.
ptpdb (https://pypi.python.org/pypi/ptpdb): Paquete separado basado en ptpython.
bpdb (https://docs.bpython-interpreter.org/bpdb.html): Incluido con bpython.

Si desean mucha más información acerca de esto, podemos recurrir a la documentación oficial. (https://docs.python.org/2/library/pdb.html)


Además de eso, también existen otros debuggers que he tenido la oportunidad de usar y son los siguientes:


Ahora si vamos a lo que nos compete.




La capacidad de escribir código de una manera eficiente yo diría que viene naturalmente con el tiempo. Me acuerdo que al principio, escribía el código todo en la clase principal o solo en un archivo. Luego aprendí conceptos que me sirvieron para ser mejor coder, como herencia, abstracción, a organizar por paquetes/módulos, entre otr@s. Vamos, recuerda tu primer programa, te aseguro que será como el mío, dan ganas de llorar :'(. Sin entrar en detalles, cuando se poseen programas con sintaxis obtusa, API's poco claras o estándares poco convenciales, es cuando entramos a mejorar. Python desde su versión más antigua hasta la versión actual(3.6.3) ha realizado muchísimas mejores en el lenguaje, para que sea más claro, más comprendible, más limpio y mucho más fácil de escribir. Les enseñaré:

• contextlib y with.
• Comprensión de listas.
• Iteradores y generadores.
• Descriptores y propiedades.
• Los Decoradores.

Python como sabrán, nos proporciona un gran conjunto de tipo de datos. De la misma forma que es para las colecciones, lo es para los tipos numéricos.
Existen algunas diferencias poco conocidas por los desarrolladores, pero no tienes más opción que aprender. Las cosas cambia cuando se trata entre colecciones y strings(cadenas). Quisiera mencionar que algunos patrones de código que parecen intuitivos y simples para los principantes(newbies) generalmente son considerados no-pythonics(no pythónicos) por los programadores más experimentados porque son ineficientes o son muy detallados. Patrones como por ejemplo: Pythonic (https://www.toptal.com/python/python-design-patterns) que es usado para resolver problemas comunes, a menudo parecen ser solo estética. Realmente está mal. Como siempre, estaremos sujetos en que la comunidad de Python esté lleno de mitos y estereotipos de cómo funcionan las cosas. Sin embargo, profundizando solo tú y nadie más que tú, podrás saber cuáles de las declaraciones que se hacen realmente son ciertas y objetar las demás.

El tema de las strings o cadenas puede resultar cierta confusión para los programadores que están acostumbrados a programar solo en la versión de Python 2. En Python 3, SOLO hay un tipo de datos capaz de almacenar información textual. Es str o simplemente, string. Es una secuencia inmutable que almacena puntos de código UNICODE. Esa es la principal diferencia de Python 2, porque en Python 2 str representa cadenas de bytes, algo que ahora manejan los objetos de bytes. Las cadenas en Python son secuencias. Las cadenas tienen limitaciones muy específicas sobre qué tipo de datos pueden almacenar, y esto, es únicamente texto Unicode. bytes y su alternativa mutable (bytearray (https://docs.python.org/3/library/functions.html#func-bytearray)) difiere de str al permitir solo bytes como un valor de secuencia-enteros en el rango 0 <= x <256. Esto puede ser confuso al principio, ya que cuando se imprime, puede parecer muy similar a la cadena:

Código (python) [Seleccionar]
print(bytes([109, 111, 114, 116, 97, 108]))


(https://i.imgur.com/45Ks6aE.png)
Ilustración 8. Resultado de el anterior print.

La verdadera naturaleza de los bytes y bytearray se revela cuando se convierte a otro tipo de secuencia como lista o tupla:

(https://i.imgur.com/egM4pr2.png)
Ilustración 9. Apreciando las diferencias.

Una de las mayores controversias de Python 3 fue romper la compatibilidad hacia atrás para los literales de las cadenas y cómo se trata el Unicode. A partir de Python 3, cada literal de cadena sin prefijo es Unicode. Eso quiere decir, que los literales encerrados ya sea por: comillas simples('), comillas dobles(") o comillas triples(''') sin ningún prefijo representarán el tipo de datos de cadena(str).

(https://i.imgur.com/KbzdyF1.png)
Ilustración 10. Verificando que se toma como Unicode lo mencionado arriba.

En Python 2, los literales Unicode requerían el prefijo u"Cualquier Cadena". Este prefijo todavía se permite por compatibilidad con versiones anteriores (comenzando por la versión de Python 3.3), pero no tiene ningún significado sintáctico en Python 3. Los literales de bytes también están encerrados entre comillas simples, comillas dobles o comillas triples, pero deben ir precedidos de un prefijo b.

(https://i.imgur.com/3r0eR36.png)
Ilustración 12. Verificando que se toma como Bytes lo mencionado arriba.

Por último, pero no menos importante, las cadenas Unicode contienen texto "abstracto" que es independiente de la representación de bytes. Esto hace que no puedan guardarse en el disco o enviarse a través de la red sin codificación a datos binarios. Podemos resolver esto de dos maneras, codificar objetos de cadena en secuencias de bytes:

➛ Usando el método str.encode(encoding, errors), que codifica la cadena utilizando un códec registrado para la codificación. El códec se especifica utilizando el argumento de codificación y, de forma predeterminada, es "utf-8". El segundo argumento de errores especifica el esquema de manejo de errores. Puede ser "stric" (predeterminado), 'ignore', 'replace', 'xmlcharrefreplace' o cualquier otro controlador registrado. Puedes consultar más acerca de los códecs en el siguiente enlace. (https://docs.python.org/2/library/codecs.html)

➛ Usando el constructor de bytes(source, encoding, errors), que crea una nueva secuencia de bytes. Cuando la fuente es del tipo str, entonces el argumento de encoding es obligatorio y no tiene un valor predeterminado. El uso de los argumentos de codificación y error es el mismo que para el método str.encode().

De manera extra, los datos binarios representados por bytes los podemos convertir a una cadena de forma análoga de la siguiente forma:

➛ Utilizando el método bytes.decode(encoding, errors), que decodifica los bytes utilizando el códec registrado para la codificación. Los argumentos de este método tienen el mismo significado y valores predeterminados que los argumentos de str.encode().

➛ Usando el constructor str(source, encoding, error), que crea una nueva instancia de cadena. Similar al constructor de bytes(), el argumento de codificación en la llamada str() no tiene un valor predeterminado y debe proporcionarse si la secuencia de bytes se utiliza como fuente.

Sabemos que las cadenas de Python son inmutables. Es lo mismo para las secuencias de bytes. Es un hecho importante porque tiene ventajas y desventajas. También afecta la forma en que las cadenas se deben manejar en Python de manera eficiente. Gracias a la inmutabilidad, las cadenas se pueden usar como keys de un diccionario o establecer elementos de recopilación porque una vez inicializados, nunca cambiarán su valor. Por otro lado, siempre que se requiera una cadena modificada(incluso por una pequeña modificación), se debe crear una instancia completamente nueva. Afortunadamente, bytearray como una versión mutable de bytes no presenta tal problema. Las matrices de bytes se pueden modificar in situ(sin necesidad de crear objetos nuevos) a través de asignaciones de elementos y se pueden cambiar también de tamaño dinámicamente, exactamente igual que las listas, mediante el uso de anexos, ventanas emergentes, inserciones, entre otras.

Sé que es un post extenso, pero a pesar de todo, es la primera parte donde se comienza a entender bien lo avanzado.

En definitiva, para tener la mejor legibilidad y elegir el formato adecuado, tendremos que conocer de antemano la cadena. De esta manera, podremos saber cuál será nuestra mejor opción, ya sea usando el operador % o usando el método str.format(). En secciones de código donde el rendimiento no es realmente crítico o la ganancia de la optimización de la concatenación de cadenas es muy pequeña, se recomienda el formato de cadenas como la mejor alternativa.

Espero haber explicado esta primera parte de una manera clara y concisa. Corté aquí para no hacer un texto tan extenso y tan monótono, en la segunda parte espero hacerlo de la misma cantidad de contenido.

Tengo muchas más cosas por dar a conocer, espero y sea de su agrado.

Cualquier duda/sugerencia/comentario es bien recibido.

Un saludo.
[/font]
Título: Re:[PARTE 1] ¡Aprende Python Avanzado!
Publicado por: n1sendev en Noviembre 05, 2017, 02:18:37 PM
Buen aporte, cuando veo mis códigos antiguos cuando estaba aprendiendo Python, me río de la mala optimización que tenia.
Pero con el tiempo se aprende.
No sabia que en Python 2.x se podía usar tambien <> como equivalente a != y eso que siempre me leo la documentación.
Y sobre la incompatibilidad de Python 3 sobre sus versiones anteriores, esta justificada, todo cambia con el tiempo, en realidad, ya estamos en la version de Python 3.7 que esta en beta aún. Pero nos quedaremos con Python 3 un largo tiempo...
Saludos!
Título: Re:[PARTE 1] ¡Aprende Python Avanzado!
Publicado por: Mortal_Poison en Noviembre 05, 2017, 10:09:49 PM

Hola n1sen, (https://underc0de.org/foro/profile/n1sen/)

Así es, concuerdo con lo que mencionas.

Un saludo.
Título: Re:[PARTE 1] ¡Aprende Python Avanzado!
Publicado por: daniel374 en Agosto 12, 2018, 03:46:37 PM
Super el aporte, siempre es bueno aprender más de este maravilloso lenguaje Python 8)