Curso
La mayoría usamos los asteriscos como operadores de multiplicación y potencia, pero en Python también tienen un amplio abanico de usos como operador de prefijo. Tras leer este artículo, conocerás a fondo el uso de los asteriscos.
Los asteriscos tienen muchos casos de uso particulares en Python. En general, estamos familiarizados con los operadores de multiplicación y potencia. Sin embargo, también pueden realizar operaciones como el desempaquetado, el paso de argumentos, etc., en diferentes situaciones. Primero, veamos el uso general de los asteriscos.
Uso general de * y **
* se usa como operador de multiplicación, mientras que ** se usa como operador de potencia.
## declaring variables
a, b = 2,3
## multiplication
print("Multiplication of 2 and 3 is {}".format(a * b))
## as power operator
print("2 power 3 is {}".format(a ** b))
Multiplication of 2 and 3 is 6
2 power 3 is 8
Ya has visto el uso general y más común de los asteriscos. Sin embargo, hay casos de uso de los asteriscos que quizá no conozcas. Vamos a descubrir ese uso "oculto" de los asteriscos.
1. Desempaquetado con *
Desempaquetar los elementos de una lista/tupla/conjunto usando *. Vamos a utilizar el desempaquetado en código más adelante para implementar otras funcionalidades de los asteriscos. El desempaquetado es la idea básica detrás del uso de los asteriscos como operadores de prefijo.
Imagina que queremos asignar el primer elemento de la lista/tupla/conjunto a a y los elementos restantes a b. En general, lo haríamos usando slicing.
## general approach
nums = [i for i in range(1, 6)]
a = nums[0]
b = nums[1:]
print(a, b)
print("--------------------------")
## hard approach
squares = [i ** 2 for i in range(1, 6)]
a = squares[0]
b = []
for i in range(1, len(squares)):
b.append(squares[i])
print(a, b)
1 [2, 3, 4, 5]
--------------------------
1 [4, 9, 16, 25]
Ahora vamos a usar el operador * para implementar esto en una sola línea de código.
nums = [i for i in range(1, 6)]
## a será 1 y b será una lista con los elementos restantes
a, *b = nums
print(a, b)
1 [2, 3, 4, 5]
Aquí a toma un valor del tipo que haya en la lista y b es una lista. El desempaquetado funciona con tuplas y conjuntos igual que con las listas. Veamos algunos ejemplos.
## using tuple
## here first element will assign to a and last element to c
a, *b, c = (1, 2, 3, 4, 5)
print(a, b, c)
print("--------------------------")
## using sets
a, *b, c = {1, 4, 9, 16, 25}
print(a, b, c)
1 [2, 3, 4] 5
--------------------------
1 [4, 9, 16] 25
1.1. Combinar iterables distintos
Podemos añadir dos iterables diferentes en listas/conjuntos/tuplas usando desempaquetado. Normalmente, tendríamos que convertir los iterables y combinarlos en uno. Pero no es necesario si usamos *. Veamos un ejemplo.
## combining a tuple, list, set
nums = [1, 2, 3]
nums2 = (4, 5, 6)
nums3 = {7, 8, 9}
## we convert the combined elements into any iterable we want
## here i am converting into a list
_list = [*nums, *nums2, *nums3]
_tuple = (*nums, *nums2, *nums3)
_set = {*nums, *nums2, *nums3}
print(type(_list))
print(_list)
print("------------------------")
print(type(_tuple))
print(_tuple)
print("------------------------")
print(type(_set))
print(_set)
<class 'list'>
[1, 2, 3, 4, 5, 6, 8, 9, 7]
------------------------
<class 'tuple'>
(1, 2, 3, 4, 5, 6, 8, 9, 7)
------------------------
<class 'set'>
{1, 2, 3, 4, 5, 6, 7, 8, 9}
1.2. Desempaquetado anidado
El desempaquetado anidado consiste en extraer elementos de un primer nivel de elementos ya extraídos. Mira el ejemplo para ver a qué nos referimos.
languages = ["Python", "HTML", "CSS", "JS"]
## unpacking
[[first_letter, *remaining], *other] = languages
print(first_letter, remaining, other)
P ['y', 't', 'h', 'o', 'n'] ['HTML', 'CSS', 'JS']
2. Desempaquetado con **
Como vimos antes, ** se usa para desempaquetar los elementos de un diccionario. Debemos conocer las claves del diccionario para extraer los valores al desempaquetar. Veamos un ejemplo.
## sample dictionary
person = {"name":"John", "age":19, "year_of_passing":"2021"}
string = "Name:-{name} Year Of Graduation:-{year_of_passing} Age:-{age}".format(**person)
print(string)
Name:-John Year Of Graduation:-2021 Age:-19
No es muy común usar ** para extraer elementos de un diccionario.
3. Asteriscos en funciones
Podemos pasar y capturar argumentos usando asteriscos en funciones para reducir líneas de código.
3.1. Desempaquetado en funciones
El operador * se utiliza para llamar a una función desempaquetando un iterable. Imagina que tenemos una lista y queremos pasar todos sus elementos por separado a print(). ¿Qué harías? Seguro que piensas en un bucle. Pero * nos lo pone muy fácil.
Pasa el iterable como *nombre_iterable. Lo desempaqueta y pasa todos los elementos por separado.
nums = [i for i in range(1, 6)]
## passsing list using the *
print(*nums)
1 2 3 4 5
nums = (i for i in range(1, 6))
## passsing tuple using the *
print(*nums, sep = ", ")
1, 2, 3, 4, 5
3.2. Empaquetar elementos
Empaquetar significa capturar varios elementos a la vez. Usamos esta característica en las llamadas a funciones.
def average(*nums):
return sum(nums) / len(nums)
Ahora podemos pasar cualquier número de enteros para calcular la media. La función average captura todos los argumentos como una lista.
## calling the average with some numbers
print(average(1, 2, 3, 4, 5))
3.0
Ya hemos visto cómo capturar múltiples elementos en una lista usando *. Ahora aprenderemos a capturar varios pares clave-valor usando **.
def _object(name, **properties):
print(name, properties)
_object("Car", color="Red", cost=999999, company="Ferrari")
Car {'ceo': 'Louis', 'color': 'Red', 'cost': 999999, 'company': 'Ferrari'}
4. Argumentos solo por palabra clave con argumentos posicionales
Los argumentos con nombre se llaman argumentos por palabra clave. Los demás son argumentos posicionales. Primero, entiende la diferencia entre argumentos por palabra clave y posicionales. Veamos un ejemplo.
## name is a positional argument
## color and cost are keyword arguments
def sample(car, color = None, cost = None):
print("Car:-{} Color:-{}, Cost:-{}".format(car, color, cost))
Podemos usar el nombre del parámetro para pasar los argumentos al llamar a una función con argumentos por palabra clave. Si no damos nada para los argumentos por palabra clave, tomarán el valor por defecto definido en la función.
Podemos especificar los argumentos por palabra clave usando las palabras clave o la posición de los argumentos.
sample("Ferrari", "Red", 999999)
Car:-Ferrari Color:-Red, Cost:-999999
sample("Ferrari", cost = 999999, color = "Red")
Car:-Ferrari Color:-Red, Cost:-999999
El orden de los argumentos por palabra clave no es esencial si los pasamos usando los nombres definidos en la función.
sample("Ferrari", color = "Green")
Car:-Ferrari Color:-Green, Cost:-None
Ahora ya tienes clara la diferencia entre argumentos posicionales y por palabra clave. Vamos al tema principal.
Los argumentos solo por palabra clave se especifican únicamente mediante palabras clave. No podemos establecerlos posicionalmente como hemos visto antes. Tenemos que declarar los argumentos por palabra clave después de los argumentos * para capturar los solo por palabra clave.
def keyword_only(*items, _list, default = False):
print(items)
print(_list)
print(default)
Tenemos que usar las palabras clave _list y default al llamar a la función, ya que van después de *items. Si no usamos las palabras clave al llamarlos, obtendremos un error. Veamos ambos casos.
nums = [i ** 2 for i in range(1, 6)]
## calling the function
keyword_only(1, 2, 3, 4, 5, _list = nums, default = True)
(1, 2, 3, 4, 5)
[1, 4, 9, 16, 25]
True
¿Y si llamamos a la función keyword_only usando argumentos posicionales?
nums = [i ** 2 for i in range(1, 6)]
## calling the function will raise an error
keyword_only(1, 2, 3, 4, 5, nums, False)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-85-bf737d8a8bfc> in <module>()
1 nums = [i ** 2 for i in range(1, 6)]
----> 2 keyword_only(1, 2, 3, 4, 5, nums, False)
TypeError: keyword_only() missing 1 required keyword-only argument: '_list'
Aparece un error indicando que falta un argumento obligatorio solo por palabra clave. Así que debemos usar las palabras clave al llamar a los argumentos solo por palabra clave. Estos argumentos se definen únicamente después de los argumentos * en las funciones.
5. Argumentos solo por palabra clave sin argumentos posicionales
Si queremos aceptar solo argumentos solo por palabra clave sin ningún argumento posicional, Python nos permite usar * en los parámetros de la función para lograrlo. Veamos un ejemplo.
def _sample(*, name):
print(name)
La función anterior acepta únicamente argumentos solo por palabra clave. Tenemos que pasar el argumento con su palabra clave. De lo contrario, obtendremos un error. El * en la definición de la función garantiza que solo acepte argumentos usando palabras clave. Cualquier parámetro que definamos después del parámetro * en la función deberá especificarse con palabras clave durante la llamada.
## calling the function using keyword 'name'
_sample(name = "Datacamp")
Datacamp
## calling the function without using keyword 'name'
## we will get a TypeError
_sample("Datacamp")
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-12-fe0d3718b56c> in <module>()
1 ## calling the function without using keyword 'name'
2 ## we will get a TypeError
----> 3 samp("Datacamp")
TypeError: samp() takes 0 positional arguments but 1 was given
Está claro que la función _sample() solo acepta argumentos solo por palabra clave*.
Conclusión
En cada nueva versión de Python se añaden funcionalidades. Los asteriscos son especiales y ayudan a reducir parte del código. No podemos aceptar múltiples argumentos posicionales sin *, y los argumentos solo por palabra clave también necesitan * para implementar esa característica. Otras operaciones con asteriscos podrían lograrse con varias líneas de código. Practícalas para interiorizarlas. ¡Gracias por leer!
Si quieres aprender más sobre Python, haz el curso Python Data Science Toolbox (Part 1) de DataCamp.
Happy Coding :)
