diff options
author | Ekaitz Zarraga <ekaitz@elenq.tech> | 2020-07-22 20:01:38 +0200 |
---|---|---|
committer | Ekaitz Zarraga <ekaitz@elenq.tech> | 2020-07-22 20:01:38 +0200 |
commit | f2df77bce2c03910aa3c031405e43b14333bac8e (patch) | |
tree | 233e10c2073d2d1ca5ef747b14ffe6766bb80a6a /es | |
parent | c3a0a74c059ac7790008e7325567317435d0c7f8 (diff) |
Corrections everywhere
Diffstat (limited to 'es')
-rw-r--r-- | es/01_intro.md | 4 | ||||
-rw-r--r-- | es/02_datos.md | 10 | ||||
-rw-r--r-- | es/03_estructura.md | 2 | ||||
-rw-r--r-- | es/04_funciones.md | 134 | ||||
-rw-r--r-- | es/05_oop.md | 293 | ||||
-rw-r--r-- | es/06_ejec_mod.md | 20 | ||||
-rw-r--r-- | es/07_install.md | 26 | ||||
-rw-r--r-- | es/08_stdlib.md | 40 | ||||
-rw-r--r-- | es/09_extralib.md | 12 | ||||
-rw-r--r-- | es/10_closing_words.md | 9 | ||||
-rw-r--r-- | es/A_devtools.md | 17 | ||||
-rw-r--r-- | es/Z_license.md | 2 |
12 files changed, 295 insertions, 274 deletions
diff --git a/es/01_intro.md b/es/01_intro.md index 84aa8a7..254f4e7 100644 --- a/es/01_intro.md +++ b/es/01_intro.md @@ -81,7 +81,7 @@ ejecución como respuesta. Este proceso se conoce como REPL, acrónimo de read-eval-print-loop (lee-evalúa-imprime-repite), aunque en otros lugares se le conoce como la shell de python. -> NOTA: La shell de python (o REPL) y la shell del sistema son cosas +> La shell de python (o REPL) y la shell del sistema son cosas > diferentes. La shell de sistema también es un intérprete pero del lenguaje > que el sistema ha definido (Bash, PowerShell...) y no suele ser capaz de > entender python. @@ -101,7 +101,7 @@ cuando la termines pulsando la tecla `ENTER` de tu teclado, recibirás el resultado de la ejecución de la orden insertada. El acrónimo REPL define el comportamiento de este ciclo a las mil maravillas: -> NOTA: En este documento, siempre que veas el símbolo `>>>` significa que se +> En este documento, siempre que veas el símbolo `>>>` significa que se > trata de un ejemplo ejecutado en la REPL. Si no lo ves, se tratará del > contenido de un archivo de código python ejecutado de forma independiente. diff --git a/es/02_datos.md b/es/02_datos.md index 48c802c..9006d2b 100644 --- a/es/02_datos.md +++ b/es/02_datos.md @@ -49,7 +49,7 @@ Traceback (most recent call last): NameError: name 'b' is not defined ``` -> NOTA: Los nombres para poder ser interpretados correctamente por python deben +> Los nombres para poder ser interpretados correctamente por python deben > cumplir unas normas estrictas: > > - No pueden tener espacios. @@ -105,7 +105,7 @@ al que una referencia tenía no habrá problemas porque es el propio dato quien guarda la información suficiente para saber cómo entenderlo. Las referencias sólo almacenan dónde se guarda este dato. -> NOTA: Seguramente te habrás dado cuenta de que el funcionamiento de python es +> Seguramente te habrás dado cuenta de que el funcionamiento de python es > más ineficiente que el de C o lenguajes similares pero mucho más flexible. Y > así es. A la hora de elegir el lenguaje debemos valorar cuál nos interesa > más para la labor que vamos a realizar. @@ -261,7 +261,7 @@ SyntaxError: invalid syntax "Tiene un apóstrofe: Luke's" ``` -> NOTA: la contrabarra sirve para introducir caracteres especiales o caracteres +> la contrabarra sirve para introducir caracteres especiales o caracteres > de escape: `\n` salto de línea, `\t` tabulador, etc. Que son una herencia de > los tiempos de las máquinas de escribir, pero son aún útiles y muy usados. > Para expresar la propia contrabarra ha de escaparse a sí misma con otra @@ -415,7 +415,7 @@ Los propios nombres de las funciones son bastante representativos de a qué tipo convierten. Si quieres saber más puedes ejecutar `help(nombre)` y ver qué te cuenta la ayuda. -> NOTA: Fíjate que si conviertes una secuencia de valores repetidos a *set* +> Fíjate que si conviertes una secuencia de valores repetidos a *set* > únicamente almacena los que no se repiten. Es uno de los usos más comunes que > tienen. @@ -629,7 +629,7 @@ que hace referencia al mismo diccionario, también ve los cambios. Esto mismo podría hacerse si se tratara de listas, ya que tienen la capacidad de alterarse a sí mismas, pero nunca podría hacerse en tuplas, porque son inmutables. -> NOTA: Los diccionarios y las listas soportan un montón de funciones y +> Los diccionarios y las listas soportan un montón de funciones y > alteraciones, no se mencionan en este apartado porque nunca terminaría. Se > dejan para el futuro y para los ejemplos que se verán durante el documento. diff --git a/es/03_estructura.md b/es/03_estructura.md index 4aa6570..1ff4a92 100644 --- a/es/03_estructura.md +++ b/es/03_estructura.md @@ -422,7 +422,7 @@ Suele utilizarse cuando no quiere tratarse una excepción o cuando se ha hecho un boceto de una función que aún no quiere desarrollarse, para que el intérprete no falle de forma inevitable. -> NOTA: Las excepciones de sintaxis son las más graves, implican que el +> Las excepciones de sintaxis son las más graves, implican que el > intérprete no es capaz de entender lo que le pedimos así que la ejecución del > programa no llega a realizarse. La sintaxis se comprueba en una etapa previa > a la ejecución. diff --git a/es/04_funciones.md b/es/04_funciones.md index 2fd3c73..4e01e55 100644 --- a/es/04_funciones.md +++ b/es/04_funciones.md @@ -327,7 +327,7 @@ que se altere, sino el contexto global, el padre de todos los contextos. 2 ``` -> NOTA: Te recomiendo, de todas formas, que nunca edites valores globales desde +> Te recomiendo, de todas formas, que nunca edites valores globales desde > el cuerpo de funciones. Es más elegante y comprensible si los efectos de las > funciones sólo se aprecian en los argumentos de entrada y salida. @@ -469,7 +469,7 @@ move_file(target="/home/guido/doc.txt", source="file.txt") # "file.txt" -> "/home/guido/doc.txt" ``` -> NOTA: Si quieres que sean obligatorios, siempre puedes lanzar una excepción. +> Si quieres que sean obligatorios, siempre puedes lanzar una excepción. Para funciones que acepten ambos tipos de argumento, es obligatorio declarar e introducir todos los argumentos posicionales primero. Es lógico, porque son @@ -534,69 +534,92 @@ Los *decorators* son un concepto que, a pesar de ser bastante concreto, nos permite descubrir todo el potencial de lo que se acaba de tratar en este apartado. Sirven para dotar a las funciones de características adicionales. -Por ejemplo, éste es un decorador que permite crear funciones que se ejecutan -en un *thread* independiente. Tiene sentido para realizar acciones de las que -se quiere que se ejecuten por su cuenta sin ralentizar el hilo principal del -programa, como el envío de un email desde un servidor web. - -``` python -import threading - -def run_in_thread(fn): - def run(*args, **kwargs): - t = threading.Thread(target=fn, args=args, kwargs=kwargs) - t.start() - return t - return run - -@run_in_thread -def send_mail(): - """ - Envía un email a un usuario, sin esperar confirmación. - """ - pass +Por ejemplo, éste es un decorador que mide el tiempo de ejecución de una +función. No lo consideres la forma adecuada para hacerlo porque no es para nada +preciso, pero es suficiente para entender el funcionamiento de lo que queremos +representar: + +``` {.python .number-lines} +>>> import time +>>> def timer(func_to_decorate): +... def decorated_function(*args, **kwargs): +... start = time.time() +... retval = func_to_decorate(*args, **kwargs) +... end = time.time() +... print("Function needed:", end - start, "s to execute") +... return retval +... return decorated_function +... +>>> @timer +... def my_function(secs): +... time.sleep(secs) +... return "whatever" +... +>>> my_function(1) +Function needed: 1.0002844333648682 s to execute +'whatever' +>>> my_function(4) +Function needed: 4.004255533218384 s to execute +'whatever' +>>> ``` Hay muchos detalles que te habrán llamado la atención del ejemplo, el uso de -`@run_in_thread` probablemente sea uno de ellos. Éste es, sin embargo, el -detalle menos importante ya que únicamente se trata de un poco de *syntactic -sugar*. +`@timer` probablemente sea uno de ellos. Éste es, sin embargo, el detalle menos +importante ya que únicamente se trata de un poco de *syntactic +sugar*[^syntactic-sugar]. -> NOTA: el *syntactic sugar* son simplificaciones sintácticas que el lenguaje -> define para acortar expresiones muy utilizadas. El ejemplo clásico de -> *syntactic sugar* es: -> `a += b` -> Que es equivalente a: -> `a = a + b` +[^syntactic-sugar]: El *syntactic sugar* son simplificaciones sintácticas que el lenguaje + define para acortar expresiones muy utilizadas. El ejemplo clásico de + *syntactic sugar* es: + `a += b` + Que es equivalente a: + `a = a + b` Los *decorators* pueden entenderse como un envoltorio para una función. No son -más que una función que devuelve otra. En el caso del decorador del ejemplo, -el *decorator* `run_in_thread` es función que recibe otra función como -argumento de entrada y devuelve la función `run`. Este decorador, al aplicarlo -a una función con `@run_in_thread` está haciendo lo siguiente: +más que una función que devuelve otra. En el caso del decorador del ejemplo, el +*decorator* `timer` es función que recibe otra función como argumento de +entrada y devuelve la función `decorated_function`. Este decorador, al +aplicarlo a una función con `@timer` está haciendo lo siguiente: ``` python -send_mail = run_in_thread(send_mail) +my_function = timer(my_function) ``` -> NOTA: `@decorator` es *syntactic sugar* de `fn = decorator(fn)`. Simplemente, +> `@decorator` es *syntactic sugar* de `fn = decorator(fn)`. Simplemente, > es más corto y más bonito. -Por lo que la función `send_mail` ya no es lo que creíamos, sino la función -`run`. En el ejemplo, la función `run` llama a la función `fn` de la función -madre (`run` es una *closure*), que resulta ser `send_mail`, a modo de thread -independiente. - -Como puedes apreciar, el hecho de capturar todos los posibles argumentos de -entrada en la función `run` permite a `run_in_thread` decorar cualquier -función, sabiendo que funcionará. - -El principal problema que los decoradores generan es que la función que hemos -decorado ya no es la que parecía ser, así que su *docstring*, sus argumentos de -entrada, etc. ya no pueden comprobarse desde la REPL usando la ayuda, ya que la -ayuda buscaría la ayuda de la función devuelta por el decorador (`run` en el -ejemplo). Usando `@functools.wraps`[^functools] podemos resolver este -problema. +Por lo que la función `my_function` ya no es lo que creíamos. Se ha sobrescrito +con `decorated_function`. En el ejemplo, la función `decorated_function` llama +a la función `function_to_decorate` de la función madre (`decorated_function` +es una *closure*). Cuando se aplica el decorador sobre `my_function`, la +función `function_to_decorate` es `my_function` para esa *closure*. + +La línea `5` del ejemplo traslada los argumentos capturados en la función +`decorated_function` a la función `function_to_decorate` usando los mismos +asteriscos que se utilizan para declarar la captura de argumentos. Estos +asteriscos son necesarios porque los argumentos en `args` y `kwargs` están +guardados en una tupla y un diccionario respectivamente y deben desempaquetarse +para que se inserten sus contenidos como argumentos de la función. De no +aplicarse, se llamaría a la función con dos argumentos: una tupla y un +diccionario. + +Al capturar todos los argumentos y pasarlos tal y como se recibieron a la +función decorada, el decorador `timer` es transparente para la función. La +función cree que ha sido llamada de forma directa aunque se haya añadido +funcionalidad alrededor de ésta que es capaz de contar el tiempo de ejecución. + +Si se desea, se pueden alterar `args` y `kwargs` para transformar los +argumentos de la llamada a la función, pero es algo que debe hacerse +cuidadosamente porque deformar las llamadas a las funciones puede confundir a +quien aplique el decorador. + +Los decoradores, usados de forma tan cruda, añaden ciertos problemas. El +principal es que la función que hemos decorado ya no es la que parecía ser, así +que su *docstring*, sus argumentos de entrada, etc. ya no pueden comprobarse +desde la REPL usando la ayuda, ya que la ayuda buscaría la ayuda de la función +devuelta por el decorador (`decorated_function` en el ejemplo). Usando +`@functools.wraps`[^functools] podemos resolver este problema. [^functools]: Puedes leer por qué y cómo en la documentación oficial de python: @@ -622,6 +645,11 @@ aquí tienes la receta: >>> help(functools.lru_cache) ``` +A parte de los decoradores de funciones, python permite decorar clases. No son +difíciles de entender una vez se conocen los decoradores de funciones así que +te animo a que los investigues cuando hayas estudiado la programación orientada +a objetos en el capítulo siguiente. + ## Lo que has aprendido diff --git a/es/05_oop.md b/es/05_oop.md index c5396b3..e45fdfd 100644 --- a/es/05_oop.md +++ b/es/05_oop.md @@ -1,22 +1,19 @@ -# Programación Orientada a Objetos +# Orientación a Objetos La *programación orientada a objetos* u *object oriented programming* (OOP) es un paradigma de programación que envuelve python de pies a cabeza. A pesar de que python se define como un lenguaje de programación multiparadigma, la -programación orientada a objetos es el paradigma principal de éste. A pesar de -que varias de las características que tratamos en el apartado anterior se -corresponden más con un lenguaje de programación funcional, en python **todo** +programación orientada a objetos es el paradigma principal de éste. Aunque +varias de las características que tratamos en el apartado anterior se +corresponden más con un lenguaje de programación funcional, en python todo (o casi todo) es una clase. -Python usa una programación orientada a objetos basada en clases[^class], a -diferencia de otros lenguajes como JavaScript, donde la orientación a objetos -está basada en prototipos[^proto]. No es el objetivo de este documento el de -contarte cuales son las diferencias entre ambas, pero es interesante que sepas -de su existencia, ya que es una de las pocas diferencias que existen entre -estos dos lenguajes de amplio uso en la actualidad. - -[^class]: <https://en.wikipedia.org/wiki/Class-based_programming> -[^proto]: <https://en.wikipedia.org/wiki/Prototype-based_programming> +Python usa una programación orientada a objetos basada en clases, a diferencia +de otros lenguajes como JavaScript, donde la orientación a objetos está basada +en prototipos. No es el objetivo de este documento el de contarte cuales son +las diferencias entre ambas, pero es interesante que sepas de su existencia, ya +que es una de las pocas diferencias que existen entre estos dos lenguajes de +amplio uso en la actualidad. ## Programación basada en clases @@ -26,7 +23,7 @@ programación basada en clases. Los objetos, *objects*, son entidades que encapsulan un estado, un comportamiento y una identidad capaz de separarlos de otras entidades. Una -clase, *class*, es la definición de estos objetos. +clase, *class*, es la descripción de estos objetos. Saliendo de la definición filosófica y trayéndola a un nivel de andar por casa, puedes aclararte sabiendo que las clases son la definición enciclopédica de @@ -68,45 +65,43 @@ La programación basada en clases se basa en tres conceptos fundamentales que repasaremos aquí de forma rápida para razonar el interés de la programación orientada a objetos sobre otros paradigmas. -La **encapsulación**[^encapsulation] trata de crear datos con sus métodos -propios para alterarlos de modo que restrinjan el acceso directo al contenido -de estos datos con el fin de asegurar una coherencia o robustez interna. Puedes +La **encapsulación** trata de crear datos que restrinjan el acceso directo su +contenido con el fin de asegurar una coherencia o robustez interna. Puedes entender esto como una forma de esconder información o como mi profesor de -programación II en la universidad solía decir: «Las patatas se pelan en la +Programación II en la universidad solía decir: «Las patatas se pelan en la cocina del restaurante, no en el comedor». La utilidad de la encapsulación es -la de aislar secciones del programa para tener total control sobre su -contenido gracias a tener total control de la vía de acceso a estos datos. A -nivel práctico este concepto puede usarse para, por ejemplo, obligar a que un -objeto sólo pueda ser alterado en incrementos controlados en lugar de poder -pisarse con un valor arbitrario. - -La **herencia**[^inheritance] es un truco para reutilizar código de forma -agresiva que, casualmente, sirve como una buena forma de razonar. Aporta la -posibilidad de crear nuevas *clases* a partir de clases ya existentes. -Volviendo a la simplificación anterior, si una clase es una definición -enciclopédica de un concepto, como un perro, puede estar basada en otra -descripción para evitar contar todo lo relacionado con ella. En el caso del -perro, el perro es un animal. Animal podría ser otra clase definida previamente -de la que el perro heredara y recibiera gran parte de su descripción genérica -para sólo cubrir puntos que necesite especificar como el tamaño, la forma, el -tipo de animal, el comportamiento concreto, etc. Existe la posibilidad de hacer -herencias múltiples también ya que algunos conceptos pueden describirse en dos +la de aislar secciones del programa para tener total control sobre su contenido +gracias a tener total control de la vía de acceso a estos datos. A nivel +práctico este concepto puede usarse para, por ejemplo, obligar a que un objeto +sólo pueda ser alterado en incrementos controlados en lugar de poder pisarse +con un valor arbitrario. + +La **herencia** es un truco para reutilizar código de forma agresiva que, +casualmente, sirve como una buena forma de razonar. Aporta la posibilidad de +crear nuevas *clases* a partir de clases ya existentes. Volviendo a la +simplificación anterior, si una clase es una definición enciclopédica de un +concepto, como un perro, puede estar basada en otra descripción para evitar +contar todo lo relacionado con ella. En el caso del perro, el perro es un +animal. Animal podría ser otra clase definida previamente de la que el perro +heredara y recibiera gran parte de su descripción genérica para sólo cubrir +puntos que necesite especificar como el tamaño, la forma, el tipo de animal, el +comportamiento concreto, etc. Existe la posibilidad de hacer herencias +múltiples también ya que algunos conceptos pueden describirse en dos superclases distintas: un perro es un animal (vive, muere, se alimenta, se reproduce) y también es terrestre (camina sobre una superficie, etc). Ambos conceptos son independientes: los coches también son terrestres pero no son animales y los peces también son animales pero no terrestres. -Y, finalmente, el **polimorfismo**[^polymorphism]. La propia etimología de la -palabra define con bastante precisión el concepto, pero aplicarlo a la -programación orientada a objetos no es tan evidente. Existen varios tipos de -polimorfismo pero el más sencillo es entender el *subtyping*[^subtyping]. Una -vez lo comprendas el resto será evidente. Si volvemos al ejemplo del perro, -para ciertos comportamientos, nos da igual que tratemos de perros, de peces o -de pájaros, todos son animales y todos los animales se comportan de la misma -forma. Es decir, todas las subclases señaladas comparten el comportamiento de -la superclase animal. Si esto es cierto, puede suponerse que en cualquier caso -en el que se espere un objeto de la clase animal es seguro usar una subclase de -ésta. +Y, finalmente, el **polimorfismo**. La propia etimología de la palabra define +con bastante precisión el concepto, pero aplicarlo a la programación orientada +a objetos no es tan evidente. Existen varios tipos de polimorfismo pero el más +sencillo es entender el *subtyping*. Una vez lo comprendas el resto será +evidente. Si volvemos al ejemplo del perro, para ciertos comportamientos, nos +da igual que tratemos de perros, de peces o de pájaros, todos son animales y +todos los animales se comportan de la misma forma. Es decir, todas las +subclases señaladas comparten el comportamiento de la superclase animal. Si +esto es cierto, puede suponerse que en cualquier caso en el que se espere un +objeto de la clase animal es seguro usar una subclase de ésta. Visto desde otra perspectiva, las subclases comparten comportamiento porque reutilizan las funciones de la clase principal o las redefinen (*herencia*), @@ -123,15 +118,10 @@ cualquier tipo de número (integer, float, complex, etc.) de la misma manera, ya que todos son números, al fin y al cabo. Entender estos conceptos a nivel intuitivo, sin necesidad de entrar en los -detalles específicos de cada uno, es interesante para cualquier programador y -facilita de forma radical la comprensión de muchas de las decisiones de diseño -tomadas en python y en proyectos relacionados aunque también, por supuesto, de -otros lenguajes y herramientas. - -[^encapsulation]: <https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)> -[^inheritance]: <https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)> -[^polymorphism]: <https://en.wikipedia.org/wiki/Polymorphism_(computer_science)> -[^subtyping]: <https://en.wikipedia.org/wiki/Subtyping> +detalles específicos de cada uno, es interesante para a la hora de diseñar +programas y facilita de forma radical la comprensión de muchas de las +decisiones de diseño tomadas en python y en proyectos relacionados aunque +también, por supuesto, de otros lenguajes y herramientas. ## Sintaxis @@ -144,7 +134,7 @@ perro (`Dog`) `bobby` ladre (`bark`) que cuando lo haga el perro `beltza`. Los métodos describen la *funcionalidad* asociada a los perros en general, pero además, la función `bark` los describe en particular, haciendo que cada perro -tome su nombre (`name`), una propiedad o dicho de otro modo, su *estado*. +tome su nombre (`name`), una propiedad, es decir: su *estado*. ``` python class Dog: @@ -154,8 +144,8 @@ class Dog: def bark(self): print("Woof! My name is " + self.name) -bobby = Dog("Bobby") # New Dog called Bobby -beltza = Dog("Beltza") # New Dog called Beltza +bobby = Dog("Bobby") # Nuevo Dog llamado "Bobby" +beltza = Dog("Beltza") # Nuevo Dog llamado "Beltza" bobby.name # Bobby beltza.name # Beltza @@ -163,8 +153,8 @@ beltza.name # Beltza bobby.type # canine beltza.type # canine -bobby.bark() # Prints "Woof! My name is Bobby" -beltza.bark() # Prints "Woof! My name is Beltza" +bobby.bark() # "Woof! My name is Bobby" +beltza.bark() # "Woof! My name is Beltza" ``` ### Creación de objetos @@ -232,13 +222,13 @@ métodos necesarios para hacerlo. Lo que ocurre es que cualquier subclase de `Terrestrial` tiene la ocasión moverse (`move`) a su manera: en el caso del perro, caminando. -> NOTA: La herencia es interesante, pero tampoco debe caerse en la psicosis de +> La herencia es interesante, pero tampoco debe caerse en la psicosis de > añadir demasiadas superclases. En ocasiones las superclases son necesarias, > sobre todo cuando aprovechar el polimorfismo facilita el trabajo, pero > usarlas de forma agresiva genera código extremadamente complejo sin razón. -### Métodos de objeto o funciones de clase: `self` +### Métodos de objeto o funciones de clase Los métodos reciben un parámetro de entrada llamado `self` que no se utiliza a la hora de llamarlos: al hacer `bobby.bark()` no se introduce ningún argumento @@ -267,9 +257,8 @@ Efectivamente, python introduce un argumento de entrada en los métodos, el argumento de entrada que por convención se suele llamar `self`. Este parámetro es el propio `bobby` en este caso. -> NOTA: Por convención se le denomina `self`. Tú le puedes llamar como te -> apetezca pero, si pretendes que otros programadores te entiendan, mejor -> `self`. +> Por convención se le denomina `self`. Tú le puedes llamar como te apetezca +> pero, si pretendes que los demás te entiendan, mejor `self`. Para explicar por qué ocurre esto es necesario diferenciar bien entre clase y objeto. Tal y como hemos hecho antes con las definiciones enciclopédicas @@ -290,7 +279,7 @@ Ahora bien, para python las funciones de clase y los métodos (de los objetos, si no no se llamarían métodos), se implementan de la misma manera. Para la clase ambas cosas son lo mismo. Sin embargo, el comportamiento del operador punto (`.`), que dice a quién pertenece la función o método, es diferente si el -valor de la izquierda es una clase o un objeto. Introduciendo en el segundo +valor de la izquierda es una clase o un objeto, introduciendo en el segundo caso el propio objeto como primer parámetro de entrada, el `self` del que hablamos, para que la clase sepa qué objeto tiene que alterar. Este es el mecanismo de la *identidad* del que antes hablamos y no llegamos a definir en @@ -319,7 +308,7 @@ forma distinta. A veces como función (*function*) y otras veces como método <class 'method'> ``` -> NOTA: También te habrás fijado, y si no lo has hecho es momento de hacerlo, +> También te habrás fijado, y si no lo has hecho es momento de hacerlo, > que los nombres de las clases empiezan por mayúscula en los ejemplos (`Dog`) > mientras que los objetos comienzan en minúscula (`bobby`). Se trata de otra > convención ampliamente utilizada para saber diferenciar entre uno y otro de @@ -348,23 +337,22 @@ class Dog: `type` es lo que se conoce como una *variable de clase* (*class variable*). -> NOTA: En este documento se ha evitado de forma premeditada usar la palabra -> *variable* para referirse a los valores y sus referencias con la intención de -> marcar la diferencia entre ambos conceptos. En este apartado, sin embargo, a -> pesar de que se siga tratando de una referencia, se usa el nombre *class -> variable* porque es como se le llama en la documentación[^class_var] y así -> será más fácil que lo encuentres si en algún momento necesitas buscar -> información al respecto. De esto ya hemos discutido en el capítulo sobre -> datos, donde decimos que *todo es una referencia*. +> En este documento se ha evitado de forma premeditada usar la palabra +> *variable* para referirse a los valores y sus referencias con la intención +> de marcar la diferencia entre ambos conceptos. En este apartado, sin +> embargo, a pesar de que se siga tratando de una referencia, se usa el nombre +> *class variable* porque es como se le llama en la documentación y así será +> más fácil que lo encuentres si en algún momento necesitas buscar información +> al respecto. De esto ya hemos discutido en el capítulo sobre datos, donde +> decimos que *todo es una referencia*. -[^class_var]: <https://docs.python.org/3/tutorial/classes.html#class-and-instance-variables> Previamente hemos hablado de que los objetos pueden tener propiedades asociadas, y cada objeto tendrá las suyas. Es decir, que cada instancia de la clase puede tener sus propias propiedades independientes. El caso que tratamos en este momento es el contrario, el `type` es un valor que comparten **todas** las instancias de `Dog`. Cualquier cambio en esos valores los verán todos los -objetos de la clase, así que hay que ser cuidadoso. +objetos de la clase, así que hay que tener cuidado. El acceso es idéntico al que ocurriría en un valor asociado al objeto, como en el caso `name` del ejemplo, pero en este caso observas que en su declaración en @@ -375,7 +363,7 @@ A parte de poder acceder a través de los objetos de la clase, es posible acceder directamente desde la clase a través de su nombre, como a la hora de acceder a las funciones de clase: `Dog.type` resultaría en `"canine"`. -> NOTA: Si en algún caso python viera que un objeto tiene propiedades y +> Si en algún caso python viera que un objeto tiene propiedades y > variables de clase definidas con el mismo nombre, cosa que no debería ocurrir > a menudo, tendrán preferencia las propiedades. @@ -401,7 +389,7 @@ entra por accidente a las secciones que empiezan por `__`. Añade `_nombredeclase` al inicio de los campos, transformando su nombre final y dificultando el acceso por accidente. -Ese acceso accidental no sólo es para que el programador no acceda, ya que, si +Ese acceso accidental no sólo es para que quien programa no acceda, ya que, si se esfuerza la suficiente, va a poder hacerlo de igual modo, si no para que el propio python no acceda al campo que no corresponde. El hecho de añadir el nombre de la clase al campo crea una brecha en la herencia, haciendo que los @@ -426,11 +414,11 @@ método que python tiene para aportar esta funcionalidad. Es interesante añadir, por otro lado, que python es un lenguaje de programación muy dinámico por lo que la propia definición de las clases, y muchas cosas más, puede alterarse una vez creadas. Esto significa que el hecho de ocultar campos -no es más que un acuerdo tácito entre programadores porque, si quisieran, -podrían definir todo de nuevo. Trucos como este sirven para que el programador -sea consciente de que está haciendo cosas que se supone que no debería hacer. -Cuando programes en python, tómate esto como pistas que te indican cómo se -supone que deberías estar usando las clases. +no es más que un acuerdo tácito entre quienes programan porque, si quisieran, +podrían definir todo de nuevo. Trucos como este sirven para que seamos +conscientes de que estamos haciendo cosas que se supone que no deberíamos +hacer. Cuando programes en python, tómate esto como pistas que te indican cómo +se supone que deberías estar usando las clases. ### Acceso a la superclase @@ -449,11 +437,11 @@ class Clase( SuperClase ): # `metodo` de `SuperClase` ``` -> NOTA: `super` busca la clase previa por preferencia, si usas herencias +> `super` busca la clase previa por preferencia, si usas herencias > múltiples y pisas los campos puede complicarse. -## Interfaces estándar: Duck Typing +## Duck Typing Una de las razones principales para usar programación orientada a objetos es que, si se eligen los métodos con precisión, pueden crearse estructuras de @@ -475,28 +463,30 @@ de crear nuevas definiciones que soporten la misma interfaz. En otros lenguajes de programación, Java, por ejemplo, existe el concepto *interfaz* que serían una especie pequeñas clases que definen qué funciones -debe cumplir una clase para que cumpla la interfaz. A la hora de crear las -clases se les puede indicar qué interfaces implementan y el lenguaje se encarga -de asegurarse de que el programador ha hecho todo como debe. +debe cumplir una clase para ser compatible con la interfaz. A la hora de crear +las clases se les puede indicar qué interfaces implementan y el lenguaje se +encarga de asegurarse de que quien programa ha hecho todo como debe. El dinamismo de python hace que esto sea mucho más flexible. Debido a que python no hace casi comprobaciones antes de ejecutarse, necesita un método para -mucho más directo. Para python, *si anda como un pato, vuela como un pato y -nada como un pato: es un pato*. +mucho más directo. Python aplica lo que se conoce como *duck typing* (el tipado +del pato), es decir, *si anda como un pato, vuela como un pato y nada como un +pato: es un pato*. -Para que los objetos creados por el programador puedan comportarse como los que -el propio sistema aporta, python define unos nombres de métodos especiales +Para que los objetos creados manualmente puedan comportarse como los que el +propio sistema aporta, python define unos nombres de métodos especiales (*special method names*). Estos métodos tienen infinidad de utilidades: que sea posible utilizarlos como iterable en un `for`, que el sistema pueda cerrarlos -de forma automática, buscar en ellos usando el operador `in`, etc. Simplemente, +de forma automática, buscar en ellos usando el operador `in`, que puedan +sumarse y restarse con los operadores de suma y resta, etc. Simplemente, el sistema define qué funciones se deben cumplir en cada uno de esos casos y cuando se encuentre con ellos intentará llamarlas automáticamente. Si el elemento no dispone de esas funciones lanzará una excepción como la que lanza cuando intentamos acceder a un método que no existe (que es básicamente lo que estamos haciendo en este caso). -En general, python, con el fin de diferenciar claramente qué nombres elige el -programador y cuales han sido seleccionados por el lenguaje, suele utilizar una +En general, python, con el fin de diferenciar claramente qué nombres se eligen +manualmente y cuales han sido seleccionados por el lenguaje, suele utilizar una convención para la nomenclatura: comienzan y terminan por: `__` A continuación se describen algunos de los nombres especiales más comunes. @@ -515,13 +505,13 @@ crear una clase que defina un objeto en el que se puede buscar necesitas que sea un *buscable*, es decir, que soporte el nombre de método especial que define ese comportamiento. -### *Representable*: `__repr__` +### *Representable* -Este método sirve para otorgar a python una forma de representar estos objetos. -Al ejecutar la función `print` o al exponer valores en la REPL (recuerda que la -P significa print), python trata de visualizarlos. +Un objeto representable es aquél que puede representarse automáticamente en +modo texto. Al ejecutar la función `print` o al exponer valores en la REPL +(recuerda que la P significa print), python trata de visualizarlos. -La el método `__repr__` se ejecuta justo antes de imprimirse el objeto, de +El método `__repr__` se ejecuta justo antes de imprimirse el objeto, de forma automática. La función requiere que se devuelva un elemento de tipo string, que será el que después se visualice. @@ -533,7 +523,7 @@ a `bobby` el resultado cambia. Como se ve en el ejemplo, es interesante tener una buena función de representación si lo que se pretende es entender el contenido de los objetos. -> NOTA: Python ya aporta una forma estándar de representar los objetos, si la +> Python ya aporta una forma estándar de representar los objetos, si la > función `__repr__` no se define simplemente se usará la forma estándar. ``` python @@ -544,14 +534,14 @@ representación si lo que se pretende es entender el contenido de los objetos. >>> bobby <__main__.Dog object at 0x7fb7fba1b908> ->>> Dog.__repr__ = lambda self: "Dog called: " + self.name +>>> Dog.__repr__ = lambda self: "Dog called " + self.name >>> bobby.name = "Bobby" >>> bobby -Dog called: Bobby +Dog called Bobby >>> ``` -### *Contable*: `__len__` +### *Contable* En python se utiliza la función `len` para comprobar la longitud de cualquier elemento contable. Por ejemplo: @@ -577,7 +567,7 @@ diccionario, tupla y lista. Como por ejemplo los ya existentes `NamedTuple`, `OrderedDict` y otros. Los métodos *buscable* e *iterable* también son muy interesantes para esta labor. -### *Buscable*: `__contains__` +### *Buscable* El método `__contains__` debe devolver `True` o `False` y recibir un argumento de entrada. Con esto el objeto será capaz de comprobarse con sentencias que @@ -592,7 +582,7 @@ True True ``` -### *Hasheable*: `__hash__` y `__eq__` +### *Hasheable* Los objetos *hasheables*, pueden convertirse a un valor numérico mediante una función *hash*. Estas funciones habilitan la existencia de los diccionarios, @@ -614,7 +604,7 @@ pueden compararse. Los objetos básicos de python son *hasheables*. -### *Iterable*: `__next__` e `__iter__` +### *Iterable* Estos métodos permiten crear objetos con los que es posible iterar en bucles `for` y otras estructuras. Por ejemplo, los archivos de texto en python @@ -691,7 +681,7 @@ Así que, si necesitas una clase con capacidad para iterarse sobre ella, puedes crear un pequeño iterable que soporte el método `__next__` y devolver una instancia nueva de éste en el método `__iter__`. -### *Creable*: `__init__` +### *Inicializable* El método `__init__` es uno de los más usados e interesantes de esta lista, esa es la razón por la que ha aparecido en más de una ocasión durante este @@ -709,9 +699,8 @@ Como se ha visto en algún ejemplo previo, el método `__init__` (es un método, porque el objeto, aunque vacío, ya está creado) puede recibir argumentos de entrada adicionales, que serán los que la llamada al nombre de la clase reciba, a la hora de crear los nuevos objetos. Es muy habitual que el inicializador -reciba argumentos de entrada, sobre todo argumentos con nombre, para que el -programador que crea las instancias tenga la opción de inicializar los campos -que le interesen. +reciba argumentos de entrada, sobre todo argumentos con nombre, para que quien +cree las instancias tenga la opción de inicializar los campos que le interesen. Volviendo a un ejemplo previo: @@ -730,36 +719,50 @@ El nombre del perro, `"Bobby"` será recibido por `__init__` en el argumento `name` e insertado al `self` mediante `self.name = name`. De este modo, esa instancia de `Dog`, `bobby`, tomará el nombre `Bobby`. -> NOTA: En muchas ocasiones, el método `__init__` inicializa a valores vacíos +> En muchas ocasiones, el método `__init__` inicializa a valores vacíos > todas las posibles propiedades del objeto con el fin de que quien lea el > código de la clase sea capaz de ver cuáles son los campos que se utilizan en > un primer vistazo. Es una buena práctica listar todos los campos posibles en > `__init__`, a pesar de que no se necesite inicializarlos aún, con el fin de > facilitar la lectura. -> NOTA: Quien tenga experiencia con C++ puede equivocarse pensando que +> Quien tenga experiencia con C++ puede equivocarse pensando que > `__init__` es un constructor. Tal y como se ha explicado anteriormente, al > método `__init__` ya llega un objeto construido. El objetivo de `__init__` es > inicializar. En python el constructor, que se encarga de crear las instancias > de la clase, es la función `__new__`. -> NOTA: Si creas una clase a partir de la herencia y sobreescribes su método +> Si creas una clase a partir de la herencia y sobreescribes su método > `__init__` es posible que tengas que llamar al método `__init__` de la > superclase para inicializar los campos asociados a la superclase. Recuerda > que puedes acceder a la superclase usando `super`. -### *Abrible* y *cerrable*: `__enter__` y `__exit__` +Los tipos básicos de python están definidos en clases también, pero su nombre +puede usarse para hacer conversiones. + +```python +>>> int("1") +1 +``` + +Para entender el funcionamiento de esa llamada, no hay más que recordar el que +el aplicar el nombre de la clase como una llamada a función sirve para crear un +objeto del tipo indicado, enviando los argumentos a su inicializador. Es decir, +simplemente soportan el método `__init__` y construyen un nuevo objeto del tipo +indicado a partir de lo que se les envía como argumento. -Estos métodos permiten que los objetos puedan ser abiertos y cerrados de forma -segura y con una sintaxis eficiente. Aunque no se van a listar en profundidad, -el objetivo de este punto es mostrar la sentencia `with` que se habilita -gracias a estos métodos y mostrar cómo facilitan la apertura y cierre. +### *Abrible* y *cerrable* + +Los objetos *abribles* y *cerrables* puedan ser abiertos y cerrados de forma +segura y con una sintaxis eficiente. Aunque no se van a detallar en +profundidad, el objetivo de este punto es mostrar la sentencia `with` que se +habilita gracias a estos métodos y mostrar cómo facilitan la apertura y cierre. El PEP 343[^pep343] muestra en detalle la implementación de la sentencia `with`. Simplificándolo y resumiéndolo, `with` sirve para abrir elementos y cerrarlos de forma automática. -> NOTA: Los PEP (*Python Enhancement Proposals*) son propuestas de mejora para +> Los PEP (*Python Enhancement Proposals*) son propuestas de mejora para > el lenguaje. Puedes consultar todos en la web de python. Son una fuente > interesante de información y conocimiento del lenguaje y de programación en > general. @@ -799,7 +802,7 @@ se muestra la equivalencia entre la sentencia `with` y el uso de `__enter__`, [^pep343]: Puedes leer el contenido completo del PEP en: <https://www.python.org/dev/peps/pep-0343/> -### *Callable*: `__call__` +### *Llamable* Queda pendiente desde el capítulo sobre funciones, responder a lo que es un *callable* o *llamable*. Una vez llegados a este punto, tiene una respuesta @@ -808,13 +811,8 @@ fácil: un *llamable* es un objeto que soporta el método `__call__`. Aunque pueda parecer sorprendente, las funciones en python también se llaman de este modo, así que realmente son objetos que se llaman porque soportan este método. Es lógico, porque las funciones, recuerda el capítulo previo, pueden -guardar valores, como el contexto en el que se crean (*closure*). Las funciones -son meros *llamables* y como tales se comportan. - -Llevado más allá, los tipos básicos de python están definidos en clases -también, lógicamente, pero pueden ser llamados para hacer conversiones tal y -como vimos en el capítulo sobre datos. Simplemente, soportan el método -`__init__`. +guardar valores, como el contexto en el que se crean (la *closure*). Las +funciones son meros *llamables* y como tales se comportan. ``` python >>> class Dog: @@ -833,7 +831,7 @@ propio objeto que está siendo llamado, el `self` que ya conocemos. Resumiendo, el método `__call__` describe cómo se comporta el objeto cuando se le aplican las paréntesis. -### *Subscriptable*: `__getitem__`, `__setitem__` y `__delitem__` +### *Subscriptable* Tal y como el método anterior describía cómo se aplican las paréntesis a un objeto, los métodos que se muestran en este apartado describen el @@ -901,7 +899,7 @@ siguiente manera: - Fin: último elemento inclusive - Salto: un único elemento en orden de cabeza a cola -> NOTA: El índice para representar el último elemento es -1, pero si se quiere +> El índice para representar el último elemento es -1, pero si se quiere > indicar como final, usar -1 descartará el último elemento porque el final no > es inclusivo. Para que sea inclusivo es necesario dejar el campo fin vacío. @@ -922,9 +920,10 @@ los campos de un objeto, si se trata de escribir suelta lanza un error de sintaxis. Para crear *slice*s de forma separada se construyen mediante la clase `slice` de la siguiente manera: `slice(inicio, fin, salto)`. -En los métodos del protocolo *subscriptable* (`__getitem__`, `__setitem__` y -`__delitem__`) a la hora de elegir un *slice* se recibe una instancia del tipo -*slice* en lugar de una selección única como en el ejemplo previo: +En los métodos que permiten a un objeto ser *subscriptable* (`__getitem__`, +`__setitem__` y `__delitem__`) a la hora de elegir un *slice* se recibe una +instancia del tipo *slice* en lugar de una selección única como en el ejemplo +previo: ``` python >>> class Dog: @@ -943,8 +942,8 @@ slice(1, 100, -9) Por complicarlo todavía más, los campos del *slice* creado desde la clase `slice` pueden ser del tipo que se quiera. El formato de los `:` es únicamente *sintactic sugar* para crear *slices* de tipo integer o string. Aunque después -es responsabilidad del quien implemente el protocolo soportar el tipo de -*slice* definido, es posible crear *slices* de lo que sea, incluso anidarlos. +es responsabilidad del quien implemente soportar el tipo de *slice* definido, +es posible crear *slices* de lo que sea, incluso anidarlos. Como ejemplo de un caso que utiliza *slices* no integer, los tipos de datos como los que te puedes encontrar en la librería `pandas` soportan *slicing* @@ -952,9 +951,9 @@ basado en claves, como si de un diccionario se tratara. ### Ejemplo de uso -Para ejemplificar varios de estos protocolos, tomamos como ejemplo una pieza de -código fuente que quien escribe este documento ha usado en alguna ocasión en su -trabajo como desarrollador. +Para ejemplificar varios de estos métodos especiales, tomamos como ejemplo una +pieza de código fuente que quien escribe este documento ha usado en alguna +ocasión en su trabajo como desarrollador. Se trata de un iterable que es capaz de iterar en un sistema de ficheros estructurado en carpetas *año-mes-día* con la estructura `AAAA/MM/DD`. Este @@ -973,7 +972,7 @@ funciones que no conozcas. Te animo a que analices el comportamiento del ejemplo, viendo en detalle cómo se comporta. Como referencia, fuera de la estructura de la clase, en las últimas líneas, tienes disponible un bucle que puedes probar a ejecutar para -ver cómo se comporta. +ver su comportamiento. ``` {.python .numberLines} from datetime import timedelta @@ -1027,7 +1026,7 @@ for i in it: print(i) ``` -#### Ejercicio libre: `yield` y los generadores +#### Ejercicio libre: generadores La parte de la iteración del ejemplo previo puede realizarse forma más breve mediante el uso de la sentencia `yield`. Aunque no la trataremos, `yield` @@ -1085,12 +1084,12 @@ ha dado una pincelada sobre ella para que tú investigues cuando lo veas necesario, pero que sepas por dónde empezar. Para describir más en detalle lo calado que está python de programación -orientada a objetos necesitabas un ejemplo mucho más agresivo: los protocolos. -A través de ellos has visto cómo python recoge las funcionalidades estándar y -te permite crear objetos que las cumplan. Además, te ha servido para ver que -**todo** en python es un objeto (hasta las clases lo son[^objects]) y para ver -formas elegantes de resolver problemas comunes, como los iteradores, `with` y -otros. +orientada a objetos necesitabas un ejemplo mucho más agresivo: los métodos con +nombres especiales. A través de ellos has visto cómo python recoge las +funcionalidades estándar y te permite crear objetos que las cumplan. Además, te +ha servido para ver que **todo** en python es un objeto (hasta las clases lo +son[^objects]) y para ver formas elegantes de resolver problemas comunes, como +los iteradores, `with` y otros. También, te recuerdo que, aunque sea de forma colateral y sin prestarle demasiada atención, se te ha sugerido que cuando programamos no lo hacemos diff --git a/es/06_ejec_mod.md b/es/06_ejec_mod.md index 0c54f4f..3a3a0ef 100644 --- a/es/06_ejec_mod.md +++ b/es/06_ejec_mod.md @@ -130,7 +130,7 @@ En este último ejemplo, se trae la clase `date` al contexto actual. También existe la posibilidad de importar más de una definición del módulo, usando la coma para separarlas, o todo lo que el módulo exponga mediante el símbolo `*`. Es peligroso, sin embargo, traer definiciones al namespace actual de forma -descuidada, sobre todo con la última opción, porque, es posible que se repitan +descuidada, sobre todo con la última opción, porque es posible que se repitan nombres por accidente y se pisen definiciones. Los namespaces se inventan con el fin de separar las definiciones y evitar colisiones de este tipo. @@ -142,7 +142,7 @@ describir dónde se buscan estos módulos. Los módulos se buscan en los siguientes lugares: 1. El directorio del fichero ejecutado o el directorio de trabajo de la REPL -2. Los directorios indicados en el entorno. +2. Los directorios indicados en el entorno 3. La configuración por defecto (depende de la instalación) Esto significa que si guardas un archivo de python en IDLE y guardas otro más @@ -153,7 +153,7 @@ vacío llamado `init.py` te permitirá hacer `import paquete`. Si añadieras má módulos dentro del paquete, podrías importar cada uno de ellos mediante `paquete.modulo`. -> NOTA: Los nombres de los ficheros deben coincidir con el el nombre del módulo +> Los nombres de los ficheros deben coincidir con el el nombre del módulo > más la extensión `.py`. En el caso de los directorios, saltar a un > subdirectorio implica acceder a un paquete, por lo que se añadirá un punto > (`.`). @@ -199,11 +199,11 @@ Ahora entiendes por qué es capaz de encontrar `datetime` en `/usr/lib/python3.6`, carpeta listada en `sys.path`, bajo el nombre `datetime.py`. -## Ejecución vs Importación: `__main__` *guard* +## Ejecución e importación A la hora de importar un módulo, python procesa el contenido de éste ya que -necesita definir las funciones, clases, valores, etc. a exportar: ejecuta el -módulo. +necesita definir las funciones, clases, valores, etc. a exportar. Para poder +hacerlo, python necesita ejecutar el módulo. Python define una forma de separar la funcionalidad del código de sus definiciones con el fin de poder crear código cuyas definiciones sean @@ -246,10 +246,10 @@ Puedes leer más sobre este tema en la documentación de python[^main-guard]. Siguiendo este concepto, también existe el un estándar de nomenclatura de ficheros. El nombre `__main__.py` hace referencia al fichero que contiene el -código que se incluiría dentro del *guard* y será el fichero que python buscará -ejecutar siempre que se le pida ejecutar un paquete o un directorio sin -especificar qué módulo debe lanzar. Por ejemplo, ejecutar `python .`[^dot] en -la shell de sistema es equivalente a ejecutar `python __main__.py`. +código principal del programa y será el fichero que python buscará ejecutar +siempre que se le pida ejecutar un paquete o un directorio sin especificar qué +módulo debe lanzar. Por ejemplo, ejecutar `python .`[^dot] en la shell de +sistema es equivalente a ejecutar `python __main__.py`. [^dot]: `.` significa directorio actual en cualquiera de los sistemas operativos comunes. diff --git a/es/07_install.md b/es/07_install.md index de3ed93..af18b1c 100644 --- a/es/07_install.md +++ b/es/07_install.md @@ -4,10 +4,10 @@ Ahora que sabes lidiar con módulos, necesitas aprender a instalarlos en tu propio sistema, porque es bastante tedioso que tengas que copiar el código de todas tus dependencias en la carpeta de tu proyecto. -En la introducción no aseguramos de instalar con python la herramienta `pip`. +En la introducción nos aseguramos de instalar con python la herramienta `pip`. Que sirve para instalar paquetes nuevos en el sistema de forma sencilla. -## Funcionamiento de `pip` +## Sobre PIP `pip` es una herramienta extremadamente flexible, capaz de instalar módulos de python de diferentes fuentes: repositorios de git, carpetas de sistema o, la @@ -29,13 +29,13 @@ importado. El *Python Package Index* o *PyPI* es un repositorio que contiene software programado en python. En él se listan miles de librerías creadas por programadores de python para que cualquiera pueda descargarlas e instalarlas. -Más adelante veremos algunas de ellas y nos acostumbraremos a usarl PyPI como +Más adelante veremos algunas de ellas y nos acostumbraremos a usar PyPI como recurso. Ahora que sabes programar en python tú también puedes publicar tus proyectos ahí para que otras personas los usen para crear los suyos. -### Reglas de instalación: `setuptools` y `setup.py` +### Reglas de instalación Para que `pip` pueda hacer su trabajo correctamente hay que indicarle cómo debe hacerlo, ya que cada paquete es un mundo y tiene necesidades distintas. El @@ -102,7 +102,7 @@ través de la configuración de `setuptools` para ajustar la herramienta a tu proyecto. -## Entornos virtuales y dependencias: `pipenv` +## Entornos virtuales La herramienta `pip` es interesante para instalar herramientas en el sistema pero tiene ciertas carencias. La primera, que no es capaz de resolver las @@ -123,10 +123,10 @@ abandones su desarrollo, te interesará limpiar sus dependencias de tu sistema, cosa complicada si `pip` no gestiona la liberación de paquetes de forma correcta. -Para evitar estos problemas y algún otro adicional, existen herramientas -adicionales que alteran el comportamiento de `pip` y del propio python, creando -lo que se conoce como *entornos virtuales* (*virtual environments*) que quedan -aislados entre ellos y el sistema. +Para evitar estos problemas y algún otro adicional, existen herramientas que +alteran el comportamiento de `pip` y del propio python, creando lo que se +conoce como *entornos virtuales* (*virtual environments*) que quedan aislados +entre ellos y el sistema. El funcionamiento de los entornos virtuales es muy sencillo. Cuando se activan, crean un nuevo contexto en el que alteran las variables de entorno que @@ -160,7 +160,7 @@ En la shell de sistema ejecutando: pip install pipenv ``` -> NOTA: en función del sistema que utilices, puede que `pip` se llame +> En función del sistema que utilices, puede que `pip` se llame > `pip3`. El funcionamiento es idéntico. ### Uso @@ -216,9 +216,9 @@ lenguajes y entornos. ## Lo que has aprendido -En este capítulo has aprendido lo necesario sobre las herramientas que rodean -a python y su uso. De este modo, no te vas a sentir perdido en un maremágnum de -nombres extraños y comandos cuando trabajes en proyectos que ya las usan. +En este capítulo has aprendido lo necesario sobre las herramientas que rodean a +python y su uso. De este modo, no te vas perder en un maremágnum de nombres +extraños y comandos cuando trabajes en proyectos que ya las usan. Más que convertirte en un experto de cómo trabajar con estas herramientas, cosa que te dará la práctica, este episodio te ha dado las referencias que necesitas diff --git a/es/08_stdlib.md b/es/08_stdlib.md index fbaa974..001cbd0 100644 --- a/es/08_stdlib.md +++ b/es/08_stdlib.md @@ -29,7 +29,7 @@ pero facilita la programación de cualquier aplicación en éste lenguaje. Los paquetes estándar de python facilitan la lectura de infinidad de tipos de fichero, la conversión y el tratamiento de datos, el acceso a la red, la ejecución concurrente, etc. por lo que librería estándar es más que suficiente -para muchas aplicaciones y no se requiere añadir módulos externos. +para muchas aplicaciones. Conocer la librería estándar y ser capaz de buscar en ella los paquetes que necesites te facilitará mucho la tarea, evitando, por un lado, que dediques @@ -47,10 +47,10 @@ la página oficial de la documentación[^stdlibdoc]. [^stdlibdoc]: <https://docs.python.org/3/library/index.html#library-index> -## Interfaz al sistema operativo: `os` +## Interfaz al sistema operativo Ya definimos previamente el concepto *interfaz* como superficie de contacto -entre dos entidades. Esta librería facilita la interacción entre python y el +entre dos entidades. El módulo `os` facilita la interacción entre python y el sistema operativo. La comunicación con el sistema operativo es primordial para cualquier lenguaje @@ -72,20 +72,20 @@ Windows. Este paquete es muy interesante para desarrollar código portable entre las diferentes plataformas. -## Funciones relacionadas con el intérprete: `sys` +## Funciones relacionadas con el intérprete -Aunque el nombre de este módulo suene complicado, su uso principal es el de -acceder a funcionalidades que el propio python controla. Concretamente, se usa -sobre todo para la recepción de argumentos de entrada al programa principal, -la redirección de entradas y salidas del programa y la terminación del -programa. +Aunque el nombre de este módulo, `sys`, suene complicado, su uso principal es +el de acceder a funcionalidades que el propio python controla. Concretamente, +se usa sobre todo para la recepción de argumentos de entrada al programa +principal, la redirección de entradas y salidas del programa y la terminación +del programa. -### Salida forzada: `sys.exit()` +### Salida forzada Para salir de forma abrupta del programa y terminar su ejecución, python -facilita la función `sys.exit()`. Al ejecutarla la +facilita la función `sys.exit()`. -### *Standard streams*: `sys.stdin`, `sys.stdout` y `sys.stderr` +### *Standard streams* Cuando se trabaja en programas que funcionan en la terminal se pueden describir tres vías de comunicación con el usuario: @@ -143,7 +143,7 @@ que se ha redireccionado la salida: ``` -### Argumentos de entrada: `sys.argv` +### Argumentos de entrada Es posible añadir argumentos de entrada a la ejecución de los programas. Piensa en el propio programa llamado python, al ejecutarlo en la shell de sistema se @@ -193,7 +193,7 @@ programas sean configurables por quien los use sin necesidad de tener que editar el código fuente, cosa que nunca debería ser necesaria a menos que exista un error en éste o quiera añadirse una funcionalidad. -## Procesamiento de argumentos de entrada: `argparse` +## Procesamiento de argumentos de entrada Como la variable `sys.argv` entrega los argumentos de entrada tal y como los recibe y no comprueba si son coherentes, python dispone de una librería @@ -205,7 +205,7 @@ Además, facilita la creación de ayudas como la que se muestra cuando ejecutas Es una librería bastante compleja con infinidad de opciones que es mejor que leas en la propia documentación cuando necesites utilizarla. -## Expresiones regulares: `re` +## Expresiones regulares Las expresiones regulares (*regular expression*, también conocidas como *regexp* y *regex*) son secuencias de caracteres que describen un patrón de @@ -213,7 +213,7 @@ búsqueda. En python se soportan mediante el módulo `re` de la librería estándar. -## Matemáticas y estadística: `math` y `statistics` +## Matemáticas y estadística El módulo `math` soporta gran cantidad de operaciones matemáticas avanzadas para coma flotante. En él puedes encontrar logaritmos, raíces, etc. @@ -225,7 +225,7 @@ Ambos módulos tienen mucho interés ya que python se usa extensivamente en el análisis de datos. Aunque tiene librerías de terceros mucho más adecuadas para esta labor, para proyectos pequeños puede que sea suficiente con estos módulos. -## Protocolos de internet: `urllib` +## Protocolos de internet `urllib` es un conjunto de paquetes que permiten seguir URLs o enlaces, principalmente para HTTP y su versión segura HTTPs. Soporta cookies, @@ -287,7 +287,7 @@ Abstract >>> ``` -## Fechas y horas: `datetime` +## Fechas y horas `datetime`, que ya ha aparecido anteriormente, es un módulo para gestión de fecha y hora. `datetime` soporta gran cantidad de operaciones y tipos de dato. @@ -297,7 +297,7 @@ restar fechas con facilidad con los operadores habituales. Con las facilidades de `datetime`, es poco probable que necesites importar una librería de gestión de fecha y hora independiente. -## Procesamiento de ficheros: `json` y `sqlite3` +## Procesamiento de ficheros Python habilita gran cantidad de procesadores de formatos de fichero, los dos que se lista en este apartado tienen especial interés. @@ -317,7 +317,7 @@ estructuras de python de forma transparente y cómoda por lo que es un aliado interesante para aplicaciones que requieren una base de datos pequeña y resiliente. -## Aritmética de coma flotante decimal: `decimal` +## Aritmética de coma flotante decimal En el apartado sobre datos tratamos la complejidad de los números de coma flotante y que su representación binaria puede dar lugar a problemas. Este diff --git a/es/09_extralib.md b/es/09_extralib.md index 65ed418..2442d73 100644 --- a/es/09_extralib.md +++ b/es/09_extralib.md @@ -13,8 +13,7 @@ la industria. SciPy es un ecosistema de librerías de cálculo que tiene como objetivo facilitar la tarea de ingenieros, científicos y matemáticos en sus -respectivos trabajos. Puedes considerarlo como un **Matlab** para python -separado por componentes independientes. +respectivos trabajos. Además de ser el nombre del ecosistema, comparte nombre con una de las librerías fundamentales de éste. El ecosistema está formado por varias @@ -39,7 +38,7 @@ librerías, entre ellas se encuentran: dispone de un ecosistema muy poderoso a su alrededor debido al auge del análisis de datos en la industria del software. Muchas librerías comparten la interfaz de Pandas por lo que tener nociones de su comportamiento abre muchas - puertas en el sector del análisis de datos. Al igual que ocurre en SciPy, Las + puertas en el sector del análisis de datos. Al igual que ocurre en SciPy, las estructuras de datos de Pandas también se basan en Numpy. - IPython: más que una librería, IPython es una herramienta. Se trata de una @@ -77,12 +76,9 @@ Beautifulsoup es una librería de procesado de HTML extremadamente potente. Simplifica el acceso a campos de ficheros HTML con una sintaxis similar a los objetos de python, permitiendo acceder a la estructura anidada mediante el uso del punto en lugar de tener que lidiar con consultas extrañas o expresiones -regulares. Esta funcionalidad puede combinarse con selectores CSS4 para un +regulares. Esta funcionalidad puede combinarse con selectores CSS para un acceso mucho más cómodo. -Muy usada en conjunción con Requests, para el desarrollo de aplicaciones de -web-scraping. Aunque, si se desea usar algo más avanzado, se recomienda usar el -framework Scrapy. ## Tratamiento de imagen: Pillow @@ -137,7 +133,7 @@ eficientes. Programar código asíncrono en python es relativamente sencillo, pero ha preferido dejarse fuera de este documento por diversas razones. Te animo a indagar en esta libreria para valorar el interés del código asíncrono. -## Interfaces gráficas: PyQt, PyGTK, wxPython, PySimpleGUI +## Interfaces gráficas A pesar de que python dispone de un módulo en su librería estándar para tratar interfaces gráficas llamado TKinter, es recomendable utilizar librerías más diff --git a/es/10_closing_words.md b/es/10_closing_words.md index 404a58d..5b4213a 100644 --- a/es/10_closing_words.md +++ b/es/10_closing_words.md @@ -103,7 +103,7 @@ tarea con elegancia. Otro detalle que has debido de observar, sobre todo porque acaba de aparecer, es la *elegancia*. La elegancia es, hasta cierto punto, subjetiva y depende del -gusto de quien la mira. Sin embargo, esto sólo es así hasta cierto punto, la +gusto de quien la mira. Sin embargo, esto sólo es así hasta cierto punto. La realidad es que alguien puede considerar algo elegante y aun así no gustarle. Python es un ejemplo de algo así. Guste o no guste, python es un lenguaje de programación elegante, cuya elegancia forma parte primordial de la filosofía @@ -113,10 +113,9 @@ El autor de este documento, por ejemplo, no es un entusiasta de python, pero a lo largo de la travesía de escribir este documento ha podido reencontrarse, una vez más, con su elegancia. -El concepto del *código pythónico* (*pythonic code*) es un resultado de esto. -Cuando se habla de código pythónico, se habla de un código que sigue los -estándares de elegancia de python. Que es bonito, comprensible y claro. Un -código que la comunidad de desarrollo de python aprobaría. +Cuando se habla de código pythónico (*pythonic code*), se habla de un código +que sigue los estándares de elegancia de python. Que es bonito, comprensible y +claro. Un código que la comunidad de desarrollo de python aprobaría. Cuando programes en python, trata de programar código pythónico, pues es esa la verdadera razón por la que se creó el lenguaje y es la forma en la que el diff --git a/es/A_devtools.md b/es/A_devtools.md index e856373..bee13af 100644 --- a/es/A_devtools.md +++ b/es/A_devtools.md @@ -1,4 +1,4 @@ -# Anexo I: Herramientas de desarrollo {-} +# Anexo I: Herramientas {-} IDLE es una herramienta de desarrollo muy limitada, suficiente para seguir los ejemplos que se recogen en este documento pero insuficiente para desarrollar @@ -22,10 +22,10 @@ editores entre sus herramientas. ### Entornos de desarrollo integrados -Quien no utiliza entornos de desarrollo avanzados (una cuestión de gusto -personal), por lo que se le hace difícil recomendar alguno en particular. Sin -embargo, el wiki de python recoge una larguísima lista de editores y entornos -de desarrollo integrado interesantes[^ides]. +Quien escribe este documento no utiliza entornos de desarrollo avanzados (una +cuestión de gusto personal), por lo que se le hace difícil recomendar alguno en +particular. Sin embargo, el wiki de python recoge una larguísima lista de +editores y entornos de desarrollo integrado interesantes[^ides]. [^ides]: <https://wiki.python.org/moin/PythonEditors> @@ -70,13 +70,12 @@ El propio diseño de python permite que sea fácilmente depurable. La REPL facilita que se pruebe la aplicación a medida que se va desarrollando, función a función, para asegurar que su comportamiento es el correcto. Además, la capacidad introspectiva del lenguaje es una buena herramienta, en conjunción -con la REPL para comprobar el comportamiento. +con la REPL, para comprobar el comportamiento. Además de estas características, la instalación de python viene acompañada del programa `pdb` (*Python Debugger*), un depurador de código similar al conocido -[GNU-Debugger](https://en.wikipedia.org/wiki/GNU_Debugger). Existen otros -depuradores de código más amigables que este, pero la realidad es que no suelen -ser necesarios. +GNU-Debugger. Existen otros depuradores de código más amigables que este, pero +la realidad es que no suelen ser necesarios. ## Testeo de aplicaciones diff --git a/es/Z_license.md b/es/Z_license.md index 46d9792..be5f359 100644 --- a/es/Z_license.md +++ b/es/Z_license.md @@ -32,7 +32,7 @@ b. **Licencia de adaptador** es aquella licencia que Usted aplica a Sus c. **Una Licencia Compatible con BY-SA** es aquella que aparece en la lista disponible en - [creativecommons.org/compatiblelicenses](//creativecommons.org/compatiblelicenses), + [creativecommons.org/compatiblelicenses](https://creativecommons.org/compatiblelicenses), aprobada por Creative Commons, como una licencia esencialmente equivalente a esta Licencia Pública. d. **Derechos de Autor y Derechos Similares** son todos aquellos |