Si bien Python nos ofrece un módulo de arrays, crear el nuestro nos da la oportunidad de entender realmente su funcionamiento interno. Al definir nuestros métodos, comprendemos mejor estructuras como la capacidad, representación en string, el manejo de índices y el reemplazo de elementos. Así que, ¡manos a la obra! Vamos a construir nuestro propio array desde cero.
¿Cómo definir la clase array?
Para comenzar, utilizamos un editor de texto como Visual Studio Code, y creamos un archivo llamado array.py. Dentro de este archivo, vamos a definir una clase Array que representará nuestro array. El corazón de esta clase está en el método constructor y los métodos que creamos para gestionarla.
__init__: Define los atributos capacidad y fill_value, con este último predeterminado a None. Los elementos del array se guardarán en una lista vacía, que inicializaremos con el fill_value.
¿Cuáles son los métodos básicos a implementar?
¿Cómo obtener y representar el tamaño?
Un array necesita conocer su tamaño o longitud con facilidad. Para ello, empleamos un método privado que solo consulta su tamaño:
def_longitud(self):returnlen(self.items)
¿Cómo representar el array como un string?
Convertir el array a su representación de cadena es útil para visualizar sus elementos:
def__str__(self):returnstr(self.items)
¿Cómo crear un iterador para el array?
Un iterador nos permite recorrer la estructura fácilmente:
def__iter__(self):returniter(self.items)
Utilizamos iter() para que Python genere un iterador de nuestros elementos.
¿Cómo acceder y modificar elementos del array?
Para obtener un elemento específico, utilizamos un método que recibe el índice como argumento:
Estos métodos son esenciales para gestionar el contenido de nuestro array.
¿Cómo probar nuestro array en Python?
Ahora que tenemos nuestra clase Array implementada, podemos probarla en la terminal de Python. Creamos una instancia de Array y comprobamos sus métodos:
from array import Array
# Crear instancia del arraymenu = Array(5)# Obtener longitudprint(len(menu))# Output: 5# Representación del arrayprint(menu)# Output: [None, None, None, None, None]# Iterar y modificar elementosfor i inrange(len(menu)): menu.setItem(i, i +1)print(menu.getItem(0))# Output: 1print(menu.getItem(2))# Output: 3
¿Cuál es el siguiente reto?
Ahora que has creado y manejado exitosamente un array, es momento de expandir tus habilidades. Te ánimo a desarrollar un método que reemplace los valores de los elementos por números aleatorios o consecutivos. Además, intenta implementar un método para sumar todos los elementos del array, independiente de que sean números o caracteres. Recuerda, recorrer el array es clave, y el uso del iterador puede facilitar esta tarea.
¡Continúa explorando y desafiando tus conocimientos! En la próxima fase, crearemos un array de dos dimensiones, llevando tu comprensión de estructuras de datos a otro nivel.
Mmmmmmmmmmmmmmmmmmmmhhhh me quedó un mal sabor de boca de esta clase, no sé si lo explicará en alguna clase posterior o no, pero en realidad los métodos que escribimos con doble guion bajo, son precisamente para poder utilizar cosas como:
menu[1]len(menu)print(menu)
Por ejemplo, si no tuviéramos el método __getitem__ en nuestra clase, no deberíamos de ser capaces de hacer print(menu[2]), pero siento que el profe lo está haciendo ver como que son dos cosas distintas, como si Python por default tuviera este comportamiento: len(menu) y es diferente a cuando nosotros hacemos menu.__len__(), PERO PARECE SER QUE NO LO ES! No son diferentes, siento que es syntactic sugar, solamente para built in methods en clases en Python.
Por ejemplo, miren, escriban el siguiente método:
def__len__(self):print('hola')return7
y manden a llamar len(menu) y van a ver que imprime 'hola' y siempre regresa ese valor 7, no importa de qué tamaño sea el array...
Tonses pues ya, eso era lo que quería decir, muchas tardes, buenas gracias.
Lo que yo entiendo es que los ejercicios fueron para comprender los métodos que tienen los arrays usando métodos ya conocidos para depués nosotros crear nuevos
Se llaman "magic" methods, son el nombre elegante que reciben algunas operaciones como +, len, la representacion de una variable cuando se llama a print. add, sizeof, str. Hay muchos otros dependiendo del tipo de variable. Un ejemplo de uso es:
Se crea una clase que representa un billetera y se quiere que al llamar len(billetera) de como resultado el dinero dentro de la billetera. Si solo se usa len(billetera) no se va a obtener el resultado porque python no sabe la longitud de un objeto, pero si se implementa la funcion sizeof para retornar el dinero dentro de la billetera, cuando se llame len(billetera) se recibira el valor esperado
++Solucion al reto:++
Clase Array:
Programa principal
Buen código, pero la función __sum__ solo funciona para array de números, arrays de string, o arrays de cadenas. Pero no funciona para cuando los datos no son iguales (obviamente). Mi recomendación es que implementes una manera de manejar esa excepción.
Durante está clase se me presentaron varios inconvenientes, entre ellos 2 particulares que tratare a continuación.
Al importar la clase array en windows me sacaba un error, mencionaba ++que el directorio se desconocía la ubicación++. creo que la razón de esto es el SO y quizás array sea una palabra reservada. Cuando cambie el nombre del fichero ya lo dejaba importar sin ningún problema. si alguien me confirma, se lo agradezco 👍
El segundo problema y el motivo de este post, fue un error en las funciones getItem y setItem, cuando realizo los cursos suelo siempre escribir en simultaneo a la par del docente y estás 2 funciones decidí nombrarlas en notación camello, es decir la segunda palabra comenzando en mayúscula. pero cuando las uso el programa me arroja un error. alguien sabe el motivo de esto? por que debe estar en minúscula ?
Antemano gracias por la respuesta.
Hola.
La convencion en python para los dunder methods (metodos que comienzan y terminan con __) es escribirlos todos en minusculas.
No es como tal un error del interprete, asi que puede que sea o un error en windows unicamente, o quiza un error de algun linter que puedas estar usando, ya que a mi me permite usar esos nombres para los metodos.
Me Sirvio En Linux xubuntu No me permitia importar
cambie el nombre como me recomendaste y me Funciono Gracias Ya me estaba empezando a frustrar
para los que les de error al importar: from array import Array
intentar cambiar el nombre del archivo array y la clases Array,
con eso funcionó para mi
sos un heroe :D
Genial eso me funcionó, gracias
Resumen:
■■■■■■■
Creando un array
Al crear el array por nosotros mismos podremos entender como es que funciona un arreglo y le podremos dar vida según nuestras necesidades. Los métodos que se crearán para el arreglo de ejemplo e incluyendo los métodos adicionales en el reto son:
Clase padre
Crearse
Longitud
Representación string
Pertenencia
índice
Reemplazo
Instancia de la clase Array
Estas funciones no sobre escriben métodos, heredan todas las funcionalidades de la clase padre, es decir, todos los métodos.
Los aprovecharemos para crear lo métodos del challenge.
Randomizar
Sumar
Nota: Recuerda que en este script estamos hablando e módulos de python por lo que es buena idea tener tu archivo __init__.py para python trate los directorios que contienen archivos como paquetes. Este archivo puede estar vacío o inicializar código para todo el paquete. ¿Quieres saber más? consulta en: Modules
Clase Padre
classArray: def __init__(self, capacity, fill_value=None):"""
capacity =Será la capacidad que deseamos almacenar en el array.fill_value=NoneExiste para que por defecto nuestro array no tenga nada."""
# CREACIÓN: # 1.Estos items se guardaran en una lista vacía, pero usaremos métodos propios.self.items=list() # 2.Llenaremos nuestra lista vacía con los valores: # generados según la capacidad deseada para el arreglo. # Se añade fill_value para darle esos espacios donde
# se almacenaran nuestros datos. # Es como hacer hoyos en la tierra donde plantar, # aún no existe la planta, pero si el espacio que ocupará.for i inrange(capacity): self.items.append(fill_value) # LONGITUD: # 1.Definimos método privado usando __len__, # usando dundders para que nadie acceda a este. # Me define la longitud del arreglo.def__len__(self):returnlen(self.items) # REPRESENTACIÓNSTRING # 1.Representación en cadena de caracteres. # Parseo el items a un str.def__str__(self):returnstr(self.items) # ITERADOR # Nos servirá para recorrer la estructura. # El método iter me permitirá recorrer la estructura con su método interno next() def __iter__(self):returniter(self.items) # OBTENERELEMENTO # Para obtener elemento necesito el elemento y el índice
# al cual llamo con la notación de [] # Con el fin de saber su ubicación
def __getitem__(self, index):return self.items[index] # REEMPLAZARELEMENTOS # Suscribimos elementos en el indice con el nuevo elemento.def__setitem__(self, index, new_item): self.items[index]= new_item
if __name__ =='__main__': arreglo =Array(3) # Ubicación en memoria
print(arreglo) #<__main__.Array object at 0x0000020954591FA0> # Me retorna los espacios vacíos del array, los hoyos de para las plantas.print(arreglo.items) #[None,None,None] # Para llenar los datos debo usar .items o lista vacía, # para poder acceder a los elementos del arreglo. # Aquí evidencio como se llenan los datos. # [1,None,None] # [1,2,None] # [1,2,3]for i inrange(0,len(arreglo.items)): arreglo.items[i]= i +1print(arreglo.items) # Usando los métodos que creamos para el arreglo.length= arreglo.items.__len__()print("El arreglo tiene como largo : "+str(length)) # Retorno un str
strings = arreglo.items.__str__()print(type(strings)) # Creo un Objeto lista iterador y lo recorro con next
iterador = arreglo.items.__iter__()print(iterador)print(next(iterador)) # Consigo el elemnto en la posición 1 consigo_elemento = arreglo.items.__getitem__(1)print(consigo_elemento) # Ingreso un elemento específicado.arreglo.items.__setitem__(1,"Arreglo terminado!")print(arreglo.items)
Instancia del padre
'''
1.Crear un array
2.Incorporar un método para poblar sus slots con números aleatorios o secuenciales
3.Incluye un método que sume todos lo valores del array.'''
from arrays_custom importArrayimport random as r
classArray_challenge(Array): def __random_sequential__(self,randomize=False, sequential=False): self.randomize= randomize
self.sequential= sequential
# Busco el largo para tener un límite claro. # Si el parámetro randomize es True entonces
# Creo números aleatorios usando l como número base
# Uso setitems para insertar elementos por cada elemento
l = self.items.__len__()ifrandomize:for index inrange(0,l): number = r.randrange(l**l) self.items.__setitem__(index, number)ifsequential: # Aquí se puede optimizar para un algoritmo de
# ordenamiento más efectivo, en función del tamaño del arreglo
# Ver por ejemplo MergeSort.self.items=sorted(self.items)return self.items def __sum_array__(self):returnsum(self.items)if __name__ =='__main__': solution =Array_challenge(3)print("""Solución:1.Creo números aleatorios y/u ordenados dependiendo de los dos parámetros(True)Así obtengo números aleatorios, o números aleatorios y ordenados secuencialmente.2.Retorno la suma de os valores aleatorios.""")print("1: ", solution.__random_sequential__(True,True))print("2: ", solution.__sum_array__())
Pensamos lo mismo ;)
con la diferencia de que en el método de reemplazo no pido lower ni upper y hago random.randint(0, self.__len__())
muy chevere...!! :D
Solución al reto aplicando también lo aprendido en el curso de Python Intermedio:
from random importrandintfrom functools import reduce
# Reto1classArray: def __init__(self, capacity, fill_value=None): self.capacity= capacity
self.items=[fill_value for i inrange(self.capacity)] # Reto2 def __random_fill__(self): self.aleatorios=[randint(0,100)for i inrange(self.capacity)]for i inrange(self.capacity): self.items[i]= self.aleatorios[i]return self.items # Reto3 def __summation__(self): all_sum =reduce(lambda a,b: a + b, self.items)return all_sum
if __name__ =='__main__': menu =Array(10)print(menu.__random_fill__())print(menu.__summation__())
Buen aporte!
Mi solución al reto:
Hola, me sale un pequeño error cuando quiero darle valores con el for al array menu. Intenté con el shell de Python y codeandolo en el VSCode. Tengo todo igual que el profe:(
Es debido a que el magic method es
def __setitem__(self, index, item):
con la i en minúscula
Agregando métodos al Array
.
Mi solución al reto fue utilizando el método __next__() el cual ayuda a recorrer los valores del elemento iterador.
def __next__(self):"""Replace and return the values of the array with a random number using __next__"""if self.n<= self.__len__()-1: self.__setitem__(self.n,randint(1,100)) self.n+=1return self.itemselse: raise StopIteration def sum(self):"""Return the sum of the elements inside the array""" self.suma=sum([x for x in self.items])return self.suma
Para sumar los elementos ya no me compliqué tanto y utilicé la built in function sum :smile
En este código a que te refieres con n?
Por si ustedes también se preguntan, porque la implementación con dunder method.
len : Nos da la capacidad de saber la longitud del array.
str : Nos permite visualizar el objeto de una forma mas amigable.
iter: Nos permite recorrer los elementos, sin él no se permitiría la indexación.
setitem: Nos permite asignar un item, sin el no podríamos asignar un valor, ni con notación menu[2] =100
getitem: Nos permite obtener un elemento por índice, sin este no podríamos visualizar los elementos, ni con notación menu[2], nótese que sin el iterador esto tampoco es posible.
¡Métodos Mágicos!
IMPORTANTE
Al terminar el reto, como minimo deben tener los siguientes metodos en su clase array, ya que en la siguiente clase seran necesarios.
classArray(object):"Represents an array." def __init__(self, capacity, fill_value =None):"""
Args:capacity(int):static size of the array.fill_value(any, optional): value at each position.Defaults to None."""
self.items=list()for i inrange(capacity): self.items.append(fill_value) def __len__(self):"""Returns capacity of the array."""returnlen(self.items) def __str__(self):"""Returns string representation of the array"""returnstr(self.items) def __iter__(self):"""Supports traversal with a for loop."""returniter(self.items) def __getitem__(self, index):"""Subscrit operator for access at index."""return self.items[index] def __setitem__(self, index, new_item):"""Subscript operator for replacement at index.""" self.items[index]= new_item
Esta en la parte de recursos por si gustan descargarlo (:
A diferencia de otros lenguajes de programación, en Python el Array y ArrayList forman una sola estructura llamada Lista.
Entiendo que una Lista no es un Array, debido a que una lista no puede garantizar que los elementos sean guardados de forma consecutiva en memoria.
Alguien sabe por que una Clase, en este caso la clase Array, garantiza que los elementos de este sean guardados de forma consecutiva ?
Como lo hace ?
y como mantiene que estos elementos sean guardados de forma consecutiva aun cuando sus valores cambien (como lo hace el metodo "setitem"?
Esto es porque desde el momento en que instanciamos la clase Array ya estamos asignando valores a sus espacios disponibles con el valor None y posteriormente solo cambiamos el valor de dichos elementos :)
Tiene que ver con como se guardan los arrays en memoria. Cuando creas un array lo que estás haciendo es apartar cierta cantidad de memoria para los datos que vas a almacenar. En el caso de python los arreglos son dinámicos( esto ya que tu no tienes que elegir en un principio el espacio del array, solo lo creas), los arreglos dinámicos son interesantes ya que lo que hacen por detrás es asignar una cantidad especifica de memoria para guardar datos, cuando este espacio se llena se crea un nuevo arreglo y se pasan los datos que tenias en el anterior.
aquí tienes una lectura donde puedes saber mas acerca de estos arreglos https://www.javadevjournal.com/data-structure/dynamic-array/
Alguien me podría explicar la línea de código de "self.items.append(fill_value)", no entiendo el por qué va el fill_value, sé que es una pregunta un poco simple pero no la capto jaja, saludos.
Hola, Armando.
fill_value es el valor que le estamos dando a todos los elementos cuando instanciamos la clase Array, es decir, que en ese caso cuando creemos un array todos sus elementos tendrán por valor None.
La sentencia self.items.append(fill_value) hace precisamente esto. Está diciendo que que al atributo items (el cual es una lista) hará un "append" (añadir) elementos con el valor de fill_value (None).
import random
classMiArray: def __init__(self, size): self.size= size
self.slots=[] def poblar_aleatorio(self, limite): self.slots=[random.randint(0, limite)for _ inrange(self.size)] def poblar_secuencial(self): self.slots=[i+1for i inrange(self.size)] def sumar_valores(self):returnsum(self.slots)#En este ejemplo, la clase MiArray tiene tres métodos principales:#El método __init__ se utiliza para inicializar la instancia de la clase y toma un parámetro
# size, que representa el tamaño del array.#El método poblar_aleatorio se encarga de poblar los slots del array con números aleatorios.# Toma un parámetro limite, que establece el rango máximo de los números aleatorios generados.#El método poblar_secuencial se utiliza para poblar los slots del array con números secuenciales,# desde 1 hasta el tamaño del array.# El método sumar_valores calcula la suma de todos los valores presentes en el array y devuelve
# el resultado.# Aquí hay un ejemplo de cómo utilizar la clase MiArray:# Crear una instancia de la clase MiArraymi_array =MiArray(5)# Poblar los slots del array con números aleatorios
mi_array.poblar_aleatorio(100)# Imprimir el array
print(mi_array.slots)# Sumar los valores del array
suma = mi_array.sumar_valores()print("Suma:", suma)
Solución al reto de arrays
import random
classarray: def __init__(self,capacity,fill_value =None): self.capacity= capacity
self.items=list()for i inrange(capacity): self.items.append(fill_value) def __len__(self):returnlen(self.items) def __str__(self)-> str:returnstr(self.items) def __iter__(self):returniter(self.items) def __getitem__(self,index):return self.items[index] def __setitem__(self,index,new_item): self.items[index]= new_item
def __randomnumbers__(self):for i inrange(self.capacity): self.__setitem__(i,random.randint(0,9))return self.items def __sumelements__(self): count =0for i inrange(self.capacity): count += self.items[i]return count
if __name__ =='__main__': menu =array(5) menu.__setitem__(0,5)print(menu.__str__())print(menu.__randomnumbers__())print(menu.__sumelements__())
def fill(self):
for i in range(self.capacity):
self.items[i] = int(random.randint(0, self.capacity))
def __sum__(self): suma =0for i in self.items: suma = suma + i
return suma
Hice correcciones con el código de Mario Castro.
from random importrandintfrom functools import reduce
classArray: def __init__(self, capacity, fill_value=None): self.capacity= capacity
self.items=list()for i inrange(capacity): self.items.append(fill_value) def __randreplace__(self, lower_random, upper_random)-> list:"""
Set a random int value in range[lower_random, upper_random] and returnnewlist."""
return[self.__setitem__(i,randint(lower_random, upper_random))for i inrange(self.__len__())] def __len__(self):returnlen(self.items) def __str__(self):returnstr(self.items) def __iter__(self):returniter(self.items) def __getitem__(self, index):return self.items[index] def __setitem__(self, index, new_item): self.items[index]= new_item
def __sumelements__(self):returnreduce(lambda a,b: a + b, self.items)
Reto
import random
classArray: def __init__(self, capacity, content =None): self.items=list() self.capacity= capacity
for i inrange(capacity): self.items.append(content) def __str__(self):returnstr(self.items) def aleatorio(self):for i inrange(self.capacity): aleatorio = random.randint(1,100) self.items[i]= aleatorio
def suma(self): sumar =0for i inrange(self.capacity): sumar = self.items[i]+ sumar
return sumar