Complete Python Guide 2025 (3/3): classes, objects and advanced topics

Complete Python Guide 2025 (3/3): classes, objects and advanced topics

In the previous parts we covered data types and operators, flow control, and functions. In this final chapter we look at **classes and objects**, **iterators**, **variable scope**, **modules and packages**, error handling with **try... except**, **reserved words**, and the **Zen of Python**.

Disclaimer: This post has been translated to English using a machine translation model. Please, let me know if you find any mistakes.

📚 **This post is part of the series _Complete Python Guide_**, divided into three chapters that are read in order:

> * Part 1: Data types

* Part 2: Operators, flow control, and functions

* 👉 **Part 3: Classes, objects, and advanced topics**

7. Classes and objectslink image 22

Python is an object-oriented programming language. Almost everything in Python is an object, with its attributes and methods.

A class is like an object constructor or a "blueprint" for creating objects.

To create a class, the reserved word class is used

	
< > Input
Python
class Clase:
variable = 'MaximoFN'
Copied

Once the class is created, an object of that class can be created

	
< > Input
Python
objeto = Clase()
Clase.variable
Copied
>_ Output
			
'MaximoFN'

Normally, classes have an initial function, which is executed when an object of the class is created. This function is called *dunder init* and is written __init__(). The *dunder init* function must always be passed the variable self, which indicates the class itself, and then the variables that you want to pass

With this function, class variables are usually initialized, or the code that is needed when an object of the class is created is executed.

	
< > Input
Python
class Persona:
def __init__(self, nombre, edad):
self.nombre = nombre
self.edad = edad
objeto_persona = Persona("Miguel", 36)
print(objeto_persona.nombre)
print(objeto_persona.edad)
Copied
>_ Output
			
Miguel
36

In addition to the initial *dunder init* function, more functions can be created. These functions are called *methods* of the class. The variable self must always be passed to these *methods*

	
< > Input
Python
class Persona:
def __init__(self, nombre, edad):
self.nombre = nombre
self.edad = edad
def saludar(self):
print(f'Hola mi nombre es {self.nombre} y tengo {self.edad} años')
objeto_persona = Persona("Miguel", 36)
objeto_persona.saludar()
Copied
>_ Output
			
Hola mi nombre es Miguel y tengo 36 años

The self variable does not have to be called self; it can have any name, but within each class it has to always be the same. However, by convention, self is usually used

	
< > Input
Python
class Persona:
def __init__(yo_mismo, nombre, edad):
yo_mismo.nombre = nombre
yo_mismo.edad = edad
def saludar(yo_mismo):
print(f'Hola mi nombre es {yo_mismo.nombre} y tengo {yo_mismo.edad} años')
objeto_persona = Persona("Miguel", 36)
objeto_persona.saludar()
Copied
>_ Output
			
Hola mi nombre es Miguel y tengo 36 años

Can the variables of the objects be modified

	
< > Input
Python
objeto_persona.nombre = 'Marta'
objeto_persona.saludar()
Copied
>_ Output
			
Hola mi nombre es Marta y tengo 36 años

Even remove them

	
< > Input
Python
del objeto_persona.nombre
Copied

The entire object can also be deleted

	
< > Input
Python
del objeto_persona
Copied

If, for example, we want to define the structure of the class, but we do not want to code its internals for the moment, we can use pass

	
< > Input
Python
class Persona:
pass
objeto_persona = Persona()
Copied

7.1. Inheritancelink image 23

Inheritance allows us to define a class that inherits all the methods and properties of another class.

The **parent class** is the class from which inheritance occurs, also called the **base class**.

The **child class** is the class that inherits from another class, also called a **derived class**.

We create a parent class

	
< > Input
Python
class Persona:
def __init__(self, nombre, apellido):
self.nombre = nombre
self.apellido = apellido
def imprimir_nombre(self):
print(f'Me llamo {self.nombre} {self.apellido}')
objeto_padre = Persona("Laura", "Perez")
objeto_padre.imprimir_nombre()
Copied
>_ Output
			
Me llamo Laura Perez

To create the child class, you must indicate in parentheses, when declaring the class, which class it inherits from

	
< > Input
Python
class Estudiante(Persona):
pass
Copied

And when creating the object of the child class, the parameters needed by the parent class are passed to it

	
< > Input
Python
objeto_hijo = Estudiante("Mariano", "Sanz")
objeto_hijo.imprimir_nombre()
Copied
>_ Output
			
Me llamo Mariano Sanz

So far, the child class has inherited the functions of the parent class, but we can modify them by rewriting them. For example, by rewriting the *dunder init* function.

If the *dunder init* function is overridden, and we want the parent class's *dunder init* function to be called, we have to call it.

For this, there are two ways, one is by using the parent class name. In this case, you need to pass the self variable.

	
< > Input
Python
class Estudiante(Persona):
def __init__(self, nombre, apellido):
Persona.__init__(self, nombre, apellido)
objeto_hijo = Estudiante("Mariano", "Sanz")
objeto_hijo.imprimir_nombre()
Copied
>_ Output
			
Me llamo Mariano Sanz

Another way is by using super(), in this case there is no need to pass the self variable to it

	
< > Input
Python
class Estudiante(Persona):
def __init__(self, nombre, apellido):
super().__init__(nombre, apellido)
objeto_hijo = Estudiante("Mariano", "Sanz")
objeto_hijo.imprimir_nombre()
Copied
>_ Output
			
Me llamo Mariano Sanz

By modifying the functions, new code can be added

	
< > Input
Python
class Estudiante(Persona):
def __init__(self, nombre, apellido, curso):
Persona.__init__(self, nombre, apellido)
self.curso = curso
def imprimir_nombre(self):
Persona.imprimir_nombre(self)
print(f'Estoy en el curso número {self.curso}')
objeto_hijo = Estudiante("Mariano", "Sanz", 4)
objeto_hijo.imprimir_nombre()
Copied
>_ Output
			
Me llamo Mariano Sanz
Estoy en el curso número 4

Finally, new methods can be added

	
< > Input
Python
class Estudiante(Persona):
def __init__(self, nombre, apellido, curso):
Persona.__init__(self, nombre, apellido)
self.curso = curso
def imprimir_nombre(self):
Persona.imprimir_nombre(self)
print(f'Estoy en el curso número {self.curso}')
def imprimir_estudiante(self):
print(f"Soy un estudiante del curso número {self.curso}")
objeto_hijo = Estudiante("Mariano", "Sanz", 4)
objeto_hijo.imprimir_nombre()
objeto_hijo.imprimir_estudiante()
Copied
>_ Output
			
Me llamo Mariano Sanz
Estoy en el curso número 4
Soy un estudiante del curso número 4

7.2. Operator overloadinglink image 24

We can define basic operations, such as addition, between several objects of a class. For example, if we have a class that represents a vector, we can define addition and multiplication between objects of that class

	
< > Input
Python
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __mul__(self, other):
return Vector(self.x * other.x, self.y * other.y)
def __str__(self):
return f"Vector ({self.x}, {self.y})"
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2) # Vector (4, 6)
print(v1 * v2) # Vector (3, 8)
Copied
>_ Output
			
Vector (4, 6)
Vector (3, 8)

All possible operator overloads are:

  • __add__(self, other): overloads the addition operator (+).
  • __sub__(self, other): overloads the subtraction operator (-).
  • __mul__(self, other): overloads the multiplication operator (*).
  • __truediv__(self, other): overloads the division operator (/).
  • __floordiv__(self, other): overloads the floor division operator (//).
  • __mod__(self, other): overloads the modulo operator (%).
  • __divmod__(self, other): overloads the divmod() function.
  • __pow__(self, other): overloads the power operator (**).
  • __lshift__(self, other): overloads the left shift operator (<<).
  • __rshift__(self, other): overloads the right shift operator (>>).
  • __and__(self, other): overloads the and operator (&).
  • __or__(self, other): overloads the or operator (|).
  • __xor__(self, other): overloads the xor operator (^).* __lt__(self, other): overloads the less-than comparison operator (<).
  • __le__(self, other): overloads the less than or equal to comparison operator (<=).
  • __eq__(self, other): overloads the equality comparison operator (==).
  • __ne__(self, other): overloads the not-equal comparison operator (!=).
  • __gt__(self, other): overloads the greater-than comparison operator (>).
  • __ge__(self, other): overloads the greater than or equal to comparison operator (>=).
  • __neg__(self): overloads the negation operator (-).
  • __pos__(self): overloads the unary plus operator (+).
  • __abs__(self): overloads the abs() function.
  • __invert__(self): overloads the inversion operator (~).
  • __complex__(self): overloads the complex() function.
  • __int__(self): overloads the int() function.
  • __float__(self): overrides the float() function.

7.3. Custom iteratorslink image 25

As we have seen in the section 2 (Tipos de datos de Python), there are some data types that can be iterated over. But we can create our own iterable class, as long as it has the functions __len__ and __getitem__

	
< > Input
Python
class custonIterator:
def __init__(self, n):
self.items = [i for i in range(n)]
def __len__(self):
return len(self.items)
def __getitem__(self, index):
return self.items[index]
iterator = custonIterator(10)
print(len(iterator)) # 10
print(iterator[0]) # 0
print(iterator[1]) # 1
Copied
>_ Output
			
10
0
1

Now we can iterate over the object of our class with for loops, for example

	
< > Input
Python
for i in iterator:
print(i, end=" ") # 0 1 2 3 4 5 6 7 8 9
Copied
>_ Output
			
0 1 2 3 4 5 6 7 8 9

7.4. Calling objects as functionslink image 26

It may be useful to invoke an object from a function as if it were a class. This can be achieved by adding the __call__ function to the class

	
< > Input
Python
class potencia:
def __init__(self, base):
self.base = base
def __call__(self, potencia):
return self.base ** potencia
potencia_cuadrado = potencia(2)
print(potencia_cuadrado(3)) # 8
Copied
>_ Output
			
8

7.5. Private attributes and functionslink image 27

When we create a class, we can make some attributes or functions private so that they cannot be accessed from outside the class; to do this, we need to add __ before the attribute or method

	
< > Input
Python
class Privados:
def __init__(self):
self.publico = "Soy público"
self.__privado = "Soy privado"
def getPrivado(self):
return self.__privado
def setPrivado(self, valor):
self.__privado = valor
def __funcion_privada(self):
return "Soy una función privada"
def funcion_publica(self):
return self.__funcion_privada()
privados = Privados()
print("Acceso al atributo publico: ", end="")
try:
print(f"{privados.publico}")
except:
print(" No se puede acceder al atributo privado")
print("Acceso al atributo privado: ", end="")
try:
print(f"{privados.__privado}")
except:
print(" No se puede acceder al atributo privado")
print("Acceso al atributo privado mediante el accesor: ", end="")
try:
print(f"{privados.getPrivado()}")
except:
print(" No se puede acceder al atributo privado mediante el accesor")
print("Llamada a la función privada: ", end="")
try:
print(f"{privados.__funcion_privada()}")
except:
print(" No se puede llamar a la función privada")
print("Llamada a la función pública: ", end="")
try:
print(f"{privados.funcion_publica()}")
except:
print(" No se puede llamar a la función pública")
Copied
>_ Output
			
Acceso al atributo publico: Soy público
Acceso al atributo privado: No se puede acceder al atributo privado
Acceso al atributo privado mediante el accesor: Soy privado
Llamada a la función privada: No se puede llamar a la función privada
Llamada a la función pública: Soy una función privada

8. Iteratorslink image 28

An iterator is an object that contains a countable number of values.

An iterator is an object that can be iterated over, which means it can go through all the elements.

Technically, in Python, an iterator is an object that implements the iterator protocol, which consists of the __iter__() and __next__() methods.

Lists, tuples, dictionaries and sets are all iterable objects. They are iterable containers from which an iterator can be obtained.

All these objects have an iter() method that is used to obtain an iterator:

	
< > Input
Python
tupla = ("manzana", "plátano", "cereza")
iterable = iter(tupla)
print(next(iterable))
print(next(iterable))
print(next(iterable))
Copied
>_ Output
			
manzana
plátano
cereza
	
< > Input
Python
string = "plátano"
iterable = iter(string)
print(next(iterable), end=' ')
print(next(iterable), end=' ')
print(next(iterable), end=' ')
print(next(iterable), end=' ')
print(next(iterable), end=' ')
print(next(iterable), end=' ')
print(next(iterable), end=' ')
Copied
>_ Output
			
p l á t a n o

The for loop actually creates an iterator object and executes the next() method on each iteration.

	
< > Input
Python
tupla = ("manzana", "plátano", "cereza")
for x in tupla:
print(x)
Copied
>_ Output
			
manzana
plátano
cereza
	
< > Input
Python
string = "plátano"
for x in string:
print(x, end=' ')
Copied
>_ Output
			
p l á t a n o

8.1. Create an iterator objectlink image 29

To create an object/class as an iterator, you need to implement the __iter__() and __next__() methods.

	
< > Input
Python
class Numeros:
def __iter__(self):
self.a = 1
return self
def __next__(self):
x = self.a
self.a += 1
return x
objeto_iterador = Numeros()
iterador = iter(objeto_iterador)
print(next(iterador), end=' ')
print(next(iterador), end=' ')
print(next(iterador), end=' ')
print(next(iterador), end=' ')
print(next(iterador), end=' ')
Copied
>_ Output
			
1 2 3 4 5

The previous example would continue forever if it had enough next() calls, or if it were used in a for loop.

To prevent the iteration from continuing forever, we can use the StopIteration statement.

In the __next__() method, we can add a termination condition to generate an error if the iteration is performed a specific number of times:

	
< > Input
Python
class Numeros:
def __iter__(self):
self.a = 1
return self
def __next__(self):
if self.a &lt;= 20:
x = self.a
self.a += 1
return x
else:
raise StopIteration
objeto_iterador = Numeros()
iterador = iter(objeto_iterador)
for x in iterador:
print(x, end=' ')
Copied
>_ Output
			
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

8.2. Iterate by obtaining the index and the valuelink image 30

We can iterate over an iterable object, obtaining its index and value in each iteration using the enumerate() method

	
< > Input
Python
string = "MaximoFN"
for index, valor in enumerate(string):
print(f"En la posición {index}, está el caracter {valor}")
Copied
>_ Output
			
En la posición 0, está el caracter M
En la posición 1, está el caracter a
En la posición 2, está el caracter x
En la posición 3, está el caracter i
En la posición 4, está el caracter m
En la posición 5, está el caracter o
En la posición 6, está el caracter F
En la posición 7, está el caracter N

8.3. Iterate simultaneously over two iterable objectslink image 31

If we have two iterable objects of the same length, we can iterate over both at the same time using the zip() method

	
< > Input
Python
string1 = 'MaximoFN__'
string2 = 'PythonPost'
if len(string1) == len(string2):
for valor1, valor2 in zip(string1, string2):
print(f"En el primer string hay {valor1}, en el segundo string hay {valor2}")
Copied
>_ Output
			
En el primer string hay M, en el segundo string hay P
En el primer string hay a, en el segundo string hay y
En el primer string hay x, en el segundo string hay t
En el primer string hay i, en el segundo string hay h
En el primer string hay m, en el segundo string hay o
En el primer string hay o, en el segundo string hay n
En el primer string hay F, en el segundo string hay P
En el primer string hay N, en el segundo string hay o
En el primer string hay _, en el segundo string hay s
En el primer string hay _, en el segundo string hay t

9. Scope of variableslink image 32

A variable is only available within the region in which it is created. This is called *scope*

9.1. Local scopelink image 33

A variable created inside a function belongs to that function’s local scope and can only be used within that function.

	
< > Input
Python
def funcion():
x = 300
print(x)
funcion()
Copied
>_ Output
			
300

The variable x is not available outside the function, but it is available to any function inside it

	
< > Input
Python
def funcion():
x = 300
def funcion_interna():
print(x)
funcion_interna()
funcion()
Copied
>_ Output
			
300

9.2. Global Scopelink image 34

A variable created in the main body of Python code is a global variable and belongs to the global scope.

Global variables are available from any scope, global and local.

	
< > Input
Python
x = 300
def funcion():
print(f'Ámbito local: {x}')
funcion()
print(f'Ámbito global: {x}')
Copied
>_ Output
			
Ámbito local: 300
Ámbito global: 300

If two variables are created, one global and one local, both with the same name, Python will create them as two different variables

	
< > Input
Python
x = 300
def funcion():
x = 200
print(f'Variable local: {x}')
funcion()
print(f'Variable global: {x}')
Copied
>_ Output
			
Variable local: 200
Variable global: 300

If a global variable needs to be created, but it is declared in the local scope, the global keyword can be used.

The global keyword makes the variable global.

	
< > Input
Python
def funcion():
global x
x = 300
funcion()
print(f'Variable global: {x}')
Copied
>_ Output
			
Variable global: 300

In addition, the use of the global keyword allows a change to be made to a global variable within a function.

	
< > Input
Python
x = 300
def funcion():
global x
x = 200
funcion()
print(f'Variable global: {x}')
Copied
>_ Output
			
Variable global: 200

10. Moduleslink image 35

A module is a file that contains a set of functions that you want to include in your application.

To create a module, simply save the code you want in a file with the .py file extension

Tip: In Jupyter notebooks (Colab is an online Jupyter notebook), if we type the character ! before a command, we can execute terminal commands

First, let's see which directory we're in; for that, we use the pwd command (*print working directory*)

	
< > Input
Python
!pwd
Copied
>_ Output
			
/home/wallabot/Documentos/web/portafolio/posts

Let's create a folder to create our modules with the mkdir (make directory) command

	
< > Input
Python
!mkdir introduccion_python
Copied

Next, let's see which files are in our folder. We will do this using the ls (*list*) command

	
< > Input
Python
!ls introduccion_python
Copied

We see that it is empty, we create a new .py file in which we are going to create our module

	
< > Input
Python
%%writefile introduccion_python/modulo1.py
def funcion_del_modulo(nombre):
print("Hola, " + nombre)
Copied
>_ Output
			
Writing introduccion_python/modulo1.py

Let's take another look at which files are in our folder

	
< > Input
Python
!ls introduccion_python
Copied
>_ Output
			
modulo1.py __pycache__

We see that a file módulo1.py has been created. We can already use it

To use an external module, you have to use the word import. To use the module's functions, you must first write the name of the module, a ., and then the name of the function you want to use.

	
< > Input
Python
import introduccion_python.modulo1
introduccion_python.modulo1.funcion_del_modulo('MaximoFN')
Copied
>_ Output
			
Hola, MaximoFN

If we want the module to have a specific name within our code, we can use the as keyword.

	
< > Input
Python
import introduccion_python.modulo1 as mod1
mod1.funcion_del_modulo('MaximoFN')
Copied
>_ Output
			
Hola, MaximoFN

If the module has several functions, but we only want to import one, we can do so by using the from and import keywords. The syntax would be

from <module> import <function>

In this case, it is not necessary to indicate the module name when calling the function

	
< > Input
Python
%%writefile introduccion_python/modulo2.py
def funcion1_del_modulo(nombre):
print("Hola, " + nombre + ", funcion 1")
def funcion2_del_modulo(nombre):
print("Hola, " + nombre + ", funcion 2")
def funcion3_del_modulo(nombre):
print("Hola, " + nombre + ", funcion 3")
Copied
>_ Output
			
Writing introduccion_python/modulo2.py
	
< > Input
Python
from introduccion_python.modulo2 import funcion2_del_modulo
funcion2_del_modulo('MaximoFN')
Copied
>_ Output
			
Hola, MaximoFN, funcion 2

Not only can we use modules created by us, but also modules already installed (built-in modules)

For example, we can use the platform module

	
< > Input
Python
import platform
x = platform.system()
x
Copied
>_ Output
			
'Linux'

10.1. Entry points: files as modules and not as scriptslink image 36

Let's now create a file called módulo3.py

	
< > Input
Python
%%writefile introduccion_python/modulo3.py
print("Hola desde modulo3")
def funcion_del_modulo():
return "Hola desde la función del modulo3"
Copied
>_ Output
			
Overwriting introduccion_python/modulo3.py

If we now import modulo3.py to use the function funcion_del_modulo, let's see what happens

	
< > Input
Python
import introduccion_python.modulo3 as mod3
print(mod3.funcion_del_modulo())
Copied
>_ Output
			
Hola desde modulo3
Hola desde la función del modulo3

We see that the print in modulo3.py has been executed, but that is not what we wanted; this is because when the file modulo3.py is called, Python executes it as a script

But what happens if we want to run introduccion_python/main.py as a script?

	
< > Input
Python
!python introduccion_python/modulo3.py
Copied
>_ Output
			
Hola desde modulo3

We see that only the print is executed, but not the funcion_del_modulo function. If we want to have the dual functionality of the modulo3.py file, that is, to be able to import it from another module without it being executed as a script and to run it on its own, and to have the function we want executed, an entry point is used. That is, use the condition if __name__ == '__main__': and then indicate what we want to be executed. Let’s see it with an example, I’m going to rewrite the modulo3.py file

	
< > Input
Python
%%writefile introduccion_python/modulo3.py
print("Hola desde modulo3")
def funcion_del_modulo():
return "Hola desde la función del modulo3"
if __name__ == "__main__":
funcion_del_modulo()
Copied
>_ Output
			
Overwriting introduccion_python/modulo3.py

If I now call main.py from another module, the print will no longer be executed

	
< > Input
Python
import introduccion_python.modulo3 as mod3
print(mod3.funcion_del_modulo())
Copied
>_ Output
			
Hola desde la función del modulo3

And if I run it as a standalone script, the module_function function will be executed

	
< > Input
Python
!python introduccion_python/modulo3.py
Copied
>_ Output
			
Hola desde modulo3

11. Packageslink image 37

In Python, we can create our own packages; to do this, we create a folder with the package name

	
< > Input
Python
!mkdir mi_paquete_de_python
Copied

We now create two files inside

	
< > Input
Python
!touch mi_paquete_de_python/modulo1.py mi_paquete_de_python/modulo2.py
Copied

And we write in them

	
< > Input
Python
%%writefile mi_paquete_de_python/modulo1.py
def funcion1():
print("Hola desde la función 1 del módulo 1")
def funcion2():
print("Hola desde la función 2 del módulo 1")
Copied
>_ Output
			
Overwriting mi_paquete_de_python/modulo1.py
	
< > Input
Python
%%writefile mi_paquete_de_python/modulo2.py
def funcion1():
print("Hola desde la función 1 del módulo 2")
def funcion2():
print("Hola desde la función 2 del módulo 2")
Copied
>_ Output
			
Overwriting mi_paquete_de_python/modulo2.py

Now we can call the functions of our package

	
< > Input
Python
from mi_paquete_de_python import modulo1 as mod1
from mi_paquete_de_python import modulo2 as mod2
mod1.funcion1()
mod1.funcion2()
mod2.funcion1()
mod2.funcion2()
Copied
>_ Output
			
Hola desde la función 1 del módulo 1
Hola desde la función 2 del módulo 1
Hola desde la función 1 del módulo 2
Hola desde la función 2 del módulo 2

But what happens if our package has dozens of files with functions that we want to use? We would have to import all the files one by one. To avoid this, you can create an __init__.py file inside the package where all this file importing is done

	
< > Input
Python
!touch mi_paquete_de_python/__init__.py
Copied
	
< > Input
Python
%%writefile mi_paquete_de_python/__init__.py
import modulo1
import modulo2
Copied
>_ Output
			
Overwriting mi_paquete_de_python/__init__.py

Now we can simply import our package, since all modules have already been imported internally.

	
< > Input
Python
import mi_paquete_de_python as mi_paquete
mi_paquete.modulo1.funcion1()
mi_paquete.modulo1.funcion2()
mi_paquete.modulo2.funcion1()
mi_paquete.modulo2.funcion2()
Copied
>_ Output
			
Hola desde la función 1 del módulo 1
Hola desde la función 2 del módulo 1
Hola desde la función 1 del módulo 2
Hola desde la función 2 del módulo 2

In this way we only have to do an import

12. Try... exceptlink image 38

When an error occurs, or an exception as it is really called, Python will normally catch it and generate an error message.

These exceptions can be handled using the try and except statements

	
< > Input
Python
try:
print(variable_no_declarada)
except:
print("Ha ocurrido una excepción")
Copied
>_ Output
			
Ha ocurrido una excepción

Given that the try block generates an error, then the except block will be executed

Without the try block, the program would crash and generate an error

You can define as many exception blocks as you want, for example, if you want to execute a special code block for a special type of error

	
< > Input
Python
try:
print(variable_no_declarada)
except NameError:
print("La variable 'variable_no_declarada' no está definida")
except:
print("Algo inesperado ha ocurrido")
Copied
>_ Output
			
La variable 'variable_no_declarada' no está definida

The word else can be used to indicate the case in which no error has occurred.

	
< > Input
Python
try:
print('MaximoFN')
except NameError:
print("Ha ocurrido una excepción")
else:
print('Todo OK')
Copied
>_ Output
			
MaximoFN
Todo OK

with the word finally a piece of code will be executed at the end whether an exception occurred or not

	
< > Input
Python
try:
print(variable_no_declarada)
except:
print("Ha ocurrido una excepción")
finally:
print("'try except' finallizado")
Copied
>_ Output
			
Ha ocurrido una excepción
'try except' finallizado

This can be useful for closing objects and cleaning up resources

	
< > Input
Python
class Clase:
variable = 'MaximoFN'
objeto = Clase()
try:
print(Clase.mi_variable)
except:
print("Ha ocurrido una excepción")
finally:
del objeto
Copied
>_ Output
			
Ha ocurrido una excepción

12.1. Create an exceptionlink image 39

As a Python developer, you can choose to raise an exception if a condition occurs.

To throw (or generate) an exception, you have to use the raise keyword

	
< > Input
Python
def division(numerador, denominador):
if denominador == 0:
raise Exception("El denominador no puede ser 0")
return numerador/denominador
print(division(10, 0))
Copied
>_ Output
			
---------------------------------------------------------------------------Exception Traceback (most recent call last)&lt;ipython-input-16-33fb6066fa78&gt; in &lt;module&gt;
5 return numerador/denominador
6
----&gt; 7 print(division(10, 0))
&lt;ipython-input-16-33fb6066fa78&gt; in division(numerador, denominador)
1 def division(numerador, denominador):
2 if denominador == 0:
----&gt; 3 raise Exception("El denominador no puede ser 0")
4
5 return numerador/denominador
Exception: El denominador no puede ser 0

You can define what type of error to generate and the text that will be shown to the user.

	
< > Input
Python
def division(numerador, denominador):
if denominador == 0:
raise TypeError("El denominador no puede ser 0")
return numerador/denominador
print(division(10, 0))
Copied
>_ Output
			
---------------------------------------------------------------------------TypeError Traceback (most recent call last)&lt;ipython-input-17-26bfa63ae44c&gt; in &lt;module&gt;
5 return numerador/denominador
6
----&gt; 7 print(division(10, 0))
&lt;ipython-input-17-26bfa63ae44c&gt; in division(numerador, denominador)
1 def division(numerador, denominador):
2 if denominador == 0:
----&gt; 3 raise TypeError("El denominador no puede ser 0")
4
5 return numerador/denominador
TypeError: El denominador no puede ser 0

13. Keywords or reserved wordslink image 40

During this post, several Python reserved words or keywords have appeared; these are a series of words reserved by Python.

Below is a list of the keywords

	
< > Input
Python
import keyword
keyword.kwlist
Copied
>_ Output
			
['False',
'None',
'True',
'and',
'as',
'assert',
'async',
'await',
'break',
'class',
'continue',
'def',
'del',
'elif',
'else',
'except',
'finally',
'for',
'from',
'global',
'if',
'import',
'in',
'is',
'lambda',
'nonlocal',
'not',
'or',
'pass',
'raise',
'return',
'try',
'while',
'with',
'yield']

14. The Zen of Pythonlink image 41

By importing the this module, we can read Python's zen, that is, its philosophy or principles

	
< > Input
Python
import this
Copied
>_ Output
			
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

Continue reading

Last posts -->

Have you seen these projects?

Gymnasia

Gymnasia Gymnasia
React Native
Expo
TypeScript
FastAPI
Next.js
OpenAI
Anthropic

Mobile personal training app with AI assistant, exercise library, workout tracking, diet and body measurements

Horeca chatbot

Horeca chatbot Horeca chatbot
Python
LangChain
PostgreSQL
PGVector
React
Kubernetes
Docker
GitHub Actions

Chatbot conversational for cooks of hotels and restaurants. A cook, kitchen manager or room service of a hotel or restaurant can talk to the chatbot to get information about recipes and menus. But it also implements agents, with which it can edit or create new recipes or menus

View all projects -->
>_ Available for projects

Do you have an AI project?

Let's talk.

maximofn@gmail.com

Machine Learning and AI specialist. I develop solutions with generative AI, intelligent agents and custom models.

Do you want to watch any talk?

Last talks -->

Do you want to improve with these tips?

Last tips -->

Use this locally

Hugging Face spaces allow us to run models with very simple demos, but what if the demo breaks? Or if the user deletes it? That's why I've created docker containers with some interesting spaces, to be able to use them locally, whatever happens. In fact, if you click on any project view button, it may take you to a space that doesn't work.

Flow edit

Flow edit Flow edit

FLUX.1-RealismLora

FLUX.1-RealismLora FLUX.1-RealismLora
View all containers -->
>_ Available for projects

Do you have an AI project?

Let's talk.

maximofn@gmail.com

Machine Learning and AI specialist. I develop solutions with generative AI, intelligent agents and custom models.

Do you want to train your model with these datasets?

short-jokes-dataset

HuggingFace

Dataset with jokes in English

Use: Fine-tuning text generation models for humor

231K rows 2 columns 45 MB
View on HuggingFace →

opus100

HuggingFace

Dataset with translations from English to Spanish

Use: Training English-Spanish translation models

1M rows 2 columns 210 MB
View on HuggingFace →

netflix_titles

HuggingFace

Dataset with Netflix movies and series

Use: Netflix catalog analysis and recommendation systems

8.8K rows 12 columns 3.5 MB
View on HuggingFace →
View more datasets -->