DEV Community

Cover image for Conceptos Avanzados de Python 🐍 para ser un profesional en el lenguaje
GeoannyCode
GeoannyCode

Posted on

Conceptos Avanzados de Python 🐍 para ser un profesional en el lenguaje

¿Cómo funciona Python?

Python es un lenguaje interpretado lo que significa que tu código es transformado por el intérprete (máquina virtual de Python) a bytecode antes de ser ejecutado por un ordenador con x sistema operativo.

Tipos de lenguaje

El bytecode

Es un lenguaje de programación de más bajo nivel que puede ser leído por un intérprete.

Aquí un ejemplo de una función pasando a bytecode.

python-bytecode

Interprete

Es la máquina virtual, traduce el bytecode a código máquina (ceros y unos).

python-maquinavirtual

¿Los lenguajes interpretados son más lentos?

La respuesta se puede resumir en la siguiente frase:

C cuando se debe y Python cuando se puede.

Garbage collector

Recuerda que el garbage collector toma los objetos y variables que no están en uso y los elimina.

¿Qué es la carpeta Pycache?

_pycache _ Es el directorio (carpeta) que contiene el bytecode (el código intermedio) que crea Python para que lo pueda leer la máquina y no tenga que convertirlo de nuevo a bytecode.

¿Cómo organizar las carpetas de tus proyectos?

Paquetes y módulos

Módulo

  • Es cualquier archivo de Python.
  • Generalmente, contiene código que se puede reutilizar.

Paquete

  • Es un conjunto de módulos en una carpeta.
  • Siempre posee el archivo __init__.py

Ejemplo:

Proyecto de exploración espacial:

proyecto_exploracion
Módulos:

nave.py → Explica el funcionamiento de la nave.

destino.py→ Calcular las coordenadas del destino.

plataforma.py→ Permite manipular la plataforma.

lanzamiento.py→ Cualidades que necesita la nave.

test.py→ Contiene funciones de prueba.

validacion.py→Verifica identidad de los pasajeros.

¿Cómo se vería esto en una estructura de carpetas?

estructura_carpetas
Donde:

README → Como trabajar con este proyecto.

.gitignore→ Ignorar archivos en repositorios GIT

venv→ Entorno virtual

exploracion_espacial→ Paquete que contiene el: __init__.py

¿Qué son los tipados?

💻 Los tipados es una clasificación de los lenguajes de programación, tenemos cuatro tipos:

  • Estático
  • Dinámico
  • Débil
  • Fuerte

El tipado del lenguaje depende de cómo trata a los tipos de datos.

El tipado estático es el que levanta un error en el tiempo de compilación, ejemplo en JAVA:

String str = "Hello" // Variable tipo String
str = 5 
// ERROR: no se puede convertir un tipo de dato en otro de esta forma.
Enter fullscreen mode Exit fullscreen mode

El tipado dinámico levantan el error en tiempo de ejecución, ejemplo en Python:

str = "Hello" # Variable tipo String
str = 5 # La variable ahora es de tipo Entero, no hay error

## TIPADO FUERTE
x = 1
y = "2"
z = x + y # ERROR: no podemos hacer estas operaciones con tipos de datos distintos entre sí
Enter fullscreen mode Exit fullscreen mode

El tipado débil es el que hace un cambio en un tipo de dato para poder operar con él, como lo hace JavaScript y PHP.

🐍 Python es un lenguaje de tipado 👾 Dinámico y 💪 Fuerte.

tipados_notipados

Tipado estático en Python (Static Typing)

Asignar tipos a las variables de Python:

tipos_variables1

Asignar tipos en funciones:

tipos_variables2

Trabajando con estructuras de datos:

tipos_variables3

Trabajar con tipos

Importar de la librería typing las clases Dict y List

from typing import Dict, List
Enter fullscreen mode Exit fullscreen mode

Definir una lista de enteros:

positives: List[int] = [1,2,3]
Enter fullscreen mode Exit fullscreen mode

Lista de diccionarios con llaves y valores

countries: List[Dict[str, str]]
Enter fullscreen mode Exit fullscreen mode

Las ventajas de utilizar tipado estático en Python:

  1. Nos aporta claridad del código.
  2. Nos da calidad a lo que estamos programando.
  3. Nuestro código va a ser más entendible a otros programadores si trabajamos con tipos.
  4. Nos va a devolver los errores antes del que programa se ejecute.

Scope: alcance de las variables

  • El Scope es el alcance que tienen las variables.
  • Depende de donde declares o inicialices una variable para saber si tienes acceso.

Local Scope

Es la región que corresponde el ámbito de una función, donde podremos tener una o más variables, las variables van a ser accesibles únicamente en esta región y no serán visibles para otras regiones

Global Scope

Al escribir una o más variables en esta región, estas podrán ser accesibles desde cualquier parte del código.

scope

Closures

Es una forma de acceder a variables de otros Scopes a través de una nested function. Se retorna la nested function y esta recuerda el valor que imprime, aunque a la hora de ejecutarla no este dentro de su alcance.

Nested Functions (Funciones anidadas)

Funciones creadas dentro de otra función:

def main():
    a = 1
    def nested(): 
        print(a)
    return nested

my_func = main()
my_func()
Enter fullscreen mode Exit fullscreen mode

Reglas para encontrar un Closure

  • Debemos tener una nested function.
  • La nested function debe referenciar un valor de un scope superior.
  • La función que envuelve a la nested function debe retornarla también.

Decoradores

Un decorador es una función que recibe como parámetro otra función, le añade cosas y retorna una función diferente.
Tienen la misma estructura que los Closures, pero en vez de variables lo que se envía es una función.
Ejemplo:

def decorador(func):
    def envoltura():
        print("Esto se añade a mi función original.")
        func()
    return envoltura

def saludo():
    print("¡Hola!")

saludo()
# Salida:
# ¡Hola!

saludo = decorador(saludo) # Se guarda la función decorada en la variable saludo
saludo()                   # La función saludo está ahora decorada
# Salida:
# Esto se añade a mi función original.
# ¡Hola!
Enter fullscreen mode Exit fullscreen mode

Se puede hacer de manera más sencilla, con azúcar sintáctica (sugar syntax): Cuando tenemos un código que está embellecido para que nosotros lo veamos de una manera más estática, ayudando a entender de manera más sencilla el código.

De esta manera, tenemos el código anterior:

def decorador(func):
    def envoltura():
        print("Esto se añade a mi función original.")
        func()
    return envoltura

def saludo():
    print("¡Hola!")
saludo = decorador(saludo) # Se guarda la función decorada en la variable saludo (se decora)

saludo()                   # La función saludo está ahora decorada

Enter fullscreen mode Exit fullscreen mode
def decorador(func):
    def envoltura():
        print("Esto se añade a mi función original.")
        func()
    return envoltura

# De esta manera se decora la función saludo (equivale a saludo = decorador(saludo) de la última línea, quedando ahora en la línea inmediata superior ):
@decorador
def saludo():
    print("¡Hola!")

saludo()                   # La función saludo está ahora decorada
Enter fullscreen mode Exit fullscreen mode

Esto permite ahorrar código al implementar características (decoradores) comunes a diferentes funciones:

def decorator_upper(func):                  # Función decoradora
    def wrapper(text):                      # Función anidada
        return func(text).upper()           # Operación que realiza el decorado a la función (func), inserta el texto a la función original. Convierte todo a mayúsculas.
    return wrapper                          # Devuelve wapper como indica la regla de los Clousures

@decorator_upper                            # Decora la función message
def message(name):
    return f'{name}, recibiste un mensaje'  # Esto es lo que realiza la función message, previo a ser decorada.

@decorator_upper                            # Decora la función warning
def warning(name):
    return f'Usa solo mayúsculas {name}'  # Esto es lo que realiza la función warning, previo a ser decorada.

print(message("Cesar")) # Output: CESAR, RECIBISTE UN MENSAJE
print(warning("Cesar")) # Output: USA SOLO MAYÚSCULAS CESAR
Enter fullscreen mode Exit fullscreen mode

Iteradores

Antes de entender qué son los iteradores, primero debemos entender a los iterables.

  • Son todos aquellos objetos que podemos recorrer en un ciclo. - Son aquellas estructuras de datos divisibles en elementos únicos que yo puedo recorrer en un ciclo.

Pero en Python las cosas no son así. Los iterables se convierten en iteradores.

Ejemplo:

# Creando un iterador

my_list = [1,2,3,4,5]
my_iter = iter(my_list)

# Iterando un iterador

print(next(my_iter))

# Cuando no quedan datos, la excepción StopIteration es elevada
Enter fullscreen mode Exit fullscreen mode
# Creando un iterador

my_list = [1,2,3,4,5]
my_iter = iter(my_list)

# Iterando un iterador

while True: #ciclo infinito
  try:
    element = next(my_iter)
    print(element)
  except StopIteration:
    break
Enter fullscreen mode Exit fullscreen mode

Momento impactante: El ciclo “for” dentro de Python, no existe. Es un while con StopIteration. 🤯🤯🤯

my_list = [1,2,3,4,5]

for element in my_list:
  print(element)

Enter fullscreen mode Exit fullscreen mode

evenNumbers.py

class EvenNumbers:
  """Clase que implementa un iterador de todos los números pares,
  o los números pares hasta un máximo
  """

  #* Constructor de la clase
  def __init__(self, max = None): #self hace referencia al objeto futuro que voy a crear con esta clase
    self.max = max


  # Método para tener elementos o atributos que voy a necesitar para que el iterador funcione
  def __iter__(self):
    self.num = 0 #Primer número par
    #* Convertir un iterable en un iterador
    return self

  # Método para tener la función "next" de Python
  def __next__(self):
    if not self.max or self.num <= self.max:
      result = self.num
      self.num += 2
      return result
    else:
      raise StopIteration
Enter fullscreen mode Exit fullscreen mode

Ventajas de usar iteradores:

  • Nos ahorra recursos.
  • Ocupan poca memoria.

Generadores

Sugar syntax de los iteradores. Los generadores son funciones que guardan un estado. Es un iterador escrito de forma más simple.

def my_gen():

  """un ejemplo de generadores"""

  print('Hello world!')
  n = 0
  yield n # es exactamente lo mismo que return pero detiene la función, cuando se vuelva a llamar a la función, seguirá desde donde se quedó

  print('Hello heaven!')
  n = 1
  yield n

  print('Hello hell!')
  n = 2
  yield n


a = my_gen()
print(next(a)) # Hello world!
print(next(a)) # Hello heaven!
print(next(a)) # Hello hell!
print(next(a)) StopIteration
Enter fullscreen mode Exit fullscreen mode

Ahora veremos un generator expression (es como list comprehension pero mucho mejor, porque podemos manejar mucha cantidad de información sin tener problemas de rendimiento):

#Generator expression

my_list = [0,1,4,7,9,10]

my_second_list = [x*2 for x in my_list] #List comprehension
my_second_gen = ()x*2 for x in my_list]) #Generator expression
Enter fullscreen mode Exit fullscreen mode

Sets

Un set es una colección desordenada de elementos únicos e inmutables.

¿Cómo se crean los sets?

my_set = {1, 2, 3, 4, 5}
print("my_set =", my_set)

my_set2 = {"Hola", 2.4, False, True}
print("my_set2 =", my_set2)

my_set3 = {3,3,2}
print("my_set3 =", my_set3)

my_set4 = {[1,2,3], [4,5,6],4}
print("my_set4 =", my_set4)
## Este último da error porque una lista es un elemento mutable
Enter fullscreen mode Exit fullscreen mode

Una llave sin los dos puntos crea un set

Se puede crear sets vacíos…

empty_set = {}
print(type(empty_set)) #<class 'dict'>

empty_set = set()
print(type(empty_set)) # <class 'set'>
Enter fullscreen mode Exit fullscreen mode

Datos a tener en cuenta:

  • Python trata las llaves como un diccionario
  • Se usa la función set() para indicar que es un set

Casting con set

Convertir un set a otra estructura de datos, o una estructura de datos a un set.

my_list = [1,1,2,3,45,324]
my_set = set(my_list)
print(my_set)

my_tulpe = ("hola", "hola", "hola", 1)
my_set2 = set(my_tulpe)
print(my_set2)
# {1, 2, 3, 324, 45}
# {1, 'hola'}
Enter fullscreen mode Exit fullscreen mode

Datos a tener en cuenta:

  • Para transformar una lista y una tupla. solo tenemos que usar el set() sobre la variable
  • El set() elimina los elementos que se repiten.

Añadir elementos a un set

my_set = {1,2,3}
print(my_set)

#añadir un elemento

my_set.add(4)
print(my_set)

#añadir múltiples elementos
my_set.update([1,2,5])

#añadir múltiples elementos
my_set.update([1,7,2], {6,8})
print(my_set)
# {1, 2, 3}
# {1, 2, 3, 4}
# {1, 2, 3, 4, 5, 6, 7, 8}
Enter fullscreen mode Exit fullscreen mode

Datos a tener en cuenta:

acá podemos apreciar de que hay métodos que se pueden combinar con los set, por ejemplo .add() y .update(). el .add para agregar un elemento y el update para agregar múltiples elementos

Borrar elementos de un set

my_set = {1,2,3,4,5,6,7}
print(my_set)

#Borrar un elemento existente
my_set.discard(1)

my_set.discard(2)
print(my_set)

#borrar un elemento inexistene
my_set.discard(10)

my_set.remove(12)
print(my_set)
# {1, 2, 3, 4, 5, 6, 7}
# {3, 4, 5, 6, 7}
# Traceback (most recent call last):
#   File "python.py", line 28, in <module>
#     my_set.remove(12)
# KeyError: 12
Enter fullscreen mode Exit fullscreen mode

Se usan los métodos discard y remove
Si el elemento no existe con remove, lanza un error de tipo KeyError. Remove, es más exigene.

Para eliminar elementos aleatorios se usa el pop() y para eliminar todos los elementos del set se usa .clear()

Operaciones con sets

Operaciones con sets

Métodos Set

from platform import system
from os import system as console_command


# Utility
def clean_screen() -> None:
  """This function is responsible for cleaning the screen."""

  if system() == 'Windows': console_command('cls')
  else: console_command('clear')


def sets() -> None:
  """Multiple Operations with sets"""
  my_set1 = {'🍎', '🍊', '🍇', '🍓', '🍈'}
  my_set2 = {'🍉', '🍊', '🍒', '🍓', '🍋'}
  print("  →  Set 1:", my_set1)
  print("  →  Set 2:", my_set2)
  print('')
  # Union
  my_set3 = my_set1 | my_set2
  print("     Union                  :", my_set3)
  # Intersection
  my_set4 = my_set1 & my_set2
  print("\n     Intersection           :", my_set4)
  # Difference
  my_set5 = my_set1 - my_set2
  print("\n     Difference set1 - set2 :", my_set5)
  my_set6 = my_set2 - my_set1
  print("\n     Difference set2 - set1 :", my_set6)
  # Symmetric Difference
  my_set7 = my_set1 ^ my_set2
  print("\n     Symmetric Difference   :", my_set7)


if __name__ == '__main__':
  clean_screen()
  print("*** O P E R A T I O N S    W I T H    S E T S ***\n")
  sets()

Enter fullscreen mode Exit fullscreen mode

Manejo de fechas

datetime es un módulo de manejo de fechas aquì un ejemplo de su uso:

import datetime

my_time = datetime.datetime.now() # hora local de mi PC u hora universal
my_date = datetime.date.today() # fecha actual

my_day = datetime.date.today()

print(my_time)
print(my_date)

print(f'Year: {my_day.year}')
print(f'Month: {my_day.month}')
print(f'Day: {my_day.day}')
Enter fullscreen mode Exit fullscreen mode

Tabla de códigos de formato para fechas y horas(los más importantes):

Formato Código
Año %Y
Mes %m
Día %d
Hora %H
Minutos %M
Segundos %S
from datetime import datetime

my_datetime = datetime.now()
print(my_datetime)

latam = my_datetime.strftime('%d/%m/%Y')
print(f'Formato LATAM: {latam}')

usa = my_datetime.strftime('%m/%d/%Y')
print(f'Formato USA: {usa}')

random_format = my_datetime.strftime('año %Y mes %m día %d')
print(f'Formato random: {random_format}')

formato_utc = datetime.utcnow()
print(f'Formato UTC: {formato_utc}')

Enter fullscreen mode Exit fullscreen mode

Este código importa la clase datetime del módulo datetime, que proporciona diversas funciones para trabajar con fechas y horas.

La primera línea crea un objeto datetime que representa la fecha y hora actual. Luego, se imprime en pantalla utilizando la función print().

La siguiente línea utiliza el método strftime() para dar formato a la fecha y hora actual en un formato específico. El método strftime() toma una cadena de formato como argumento, que indica cómo se deben mostrar los distintos componentes de la fecha y hora (día, mes, año, etc.). En este caso, el formato especificado es '%d/%m/%Y', que indica que se deben mostrar el día, mes y año en ese orden, separados por barras.

La siguiente línea hace lo mismo, pero con un formato distinto: '%m/%d/%Y', que indica que se deben mostrar el mes, día y año en ese orden, separados por barras.

La línea siguiente también utiliza el método strftime(), pero con un formato más personalizado: 'año %Y mes %m día %d', que muestra el año, mes y día en ese orden, con las palabras "año", "mes" y "día" incluidas en la cadena de formato.

Por último, se crea un nuevo objeto datetime que representa la fecha y hora actual en el formato UTC (Coordinated Universal Time, o Tiempo Universal Coordinado), que es una referencia de tiempo uniforme utilizada a nivel internacional. Luego, se imprime en pantalla utilizando la función print().

¡Gracias por leer este post!😊

Si te ha parecido útil o interesante, puedes darle un like 💙 para motivarme a seguir compartiendo contenido así. Si tienes la oportunidad y quieres apoyar mi trabajo, puedes hacerlo a través de un pequeño donativo 💸 ¡Un café virtual sería un gran detalle! ☕ Puedes hacerlo a través de Ko-fi.

Además, si te gustaría seguir mis actualizaciones y contenido en redes sociales, puedes encontrarme en Twitter y YouTube como "GeoannyCode" 📱💻 Espero verte por allí! 😊 ¡Gracias por tu apoyo! ❤️"

Oldest comments (1)

Collapse
 
maxwellnewage profile image
Maximiliano Burgos

buen artículo! No sabía lo del for, que locura!