class Producto:
def __init__(self, codigo, nombre, precio):
self.__codigo = codigo
self.__nombre = nombre
self.__precio = precio
@property # estas tres líneas son un Getter
def codigo(self):
return self.__codigo
@codigo.setter # estas tres líneas son un Setter
def codigo(self, valor):
self.__codigo = valor
@property
def nombre(self):
return self.__nombre
@nombre.setter
def nombre(self, valor):
self.__nombre = valor
@property
def precio(self):
return self.__precio
@precio.setter
def precio(self, valor):
self.__precio = valor
def calcular_total(self, unidades):
return self.__precio * unidades
def __str__(self):
return f"Código: {self.__codigo}, Nombre: {self.__nombre}, Precio: {self.__precio}"
p1 = Producto(1, 'Producto 1', 10)
p2 = Producto(2, 'Producto 2', 20)
p3 = Producto(3, 'Producto 3', 30)
print(p1)
print(p2)
print(p3)
Código: 1, Nombre: Producto 1, Precio: 10 Código: 2, Nombre: Producto 2, Precio: 20 Código: 3, Nombre: Producto 3, Precio: 30
print(p1.nombre) # vemos que funciona el Get
Producto 1
p1.nombre = "Tiritas" # vemos que funciona el Set
print(p1.nombre)
Tiritas
p2.nombre="Vitaminas" # cambiamos el nombre del Producto 2
p3.nombre="Crema" # cambiamos el nombre del Producto 3
print(p1.calcular_total(2)) # utilizamos el método calcular_total
print(p2.calcular_total(2)) # que proporciona el producto de
print(p3.calcular_total(2)) # precio por unidades
20 40 60
print(p1) # es necesario poner un print para que actúe el método __str__
Código: 1, Nombre: Tiritas, Precio: 10
p1 # si no se pone el print no actúa el método __str__
<__main__.Producto at 0x78a178d21fc0>
class Pedido:
def __init__(self, productos, cantidades):
self.__productos = productos
self.__cantidades = cantidades
def total_pedido(self):
total = 0
for (p,c) in zip(self.__productos, self.__cantidades): # así podemos recorrer los dos arrays a la vez
total += p.calcular_total(c) # zip une los arrays
return total
def mostrar_pedido(self):
for (p,c) in zip(self.__productos, self.__cantidades):
print(f"Producto: ({p}) → {c} unidades") # los paréntesis son un adorno para resaltar el producto
prod = [p1, p2, p3] # creamos una lista con los productos que ya teníamos instanciados
cant = [2, 3, 4] # las cantidades son números
pedido1 = Pedido(prod, cant) # creamos un pedido usando como argumentos la lista de pedidos y la lista de cantidades
pedido1.total_pedido() # llama al método total_pedido que a su vez llama a calcular_total para cada producto y suma
# 200 = 10*2 + 20*3 + 30*4
200
pedido1.mostrar_pedido()
Producto: (Código: 1, Nombre: Tiritas, Precio: 10) → 2 unidades Producto: (Código: 2, Nombre: Vitaminas, Precio: 20) → 3 unidades Producto: (Código: 3, Nombre: Crema, Precio: 30) → 4 unidades
Siguiendo con la clase Pedido, añade la siguiente funcionalidad.
class Pedido:
def __init__(self): # hemos eliminado los atributos, ya no nos los han de pasar
self.__productos = [] # ahora los productos son una lista vacía
self.__cantidades = [] # iremos añadiendo productos y cantidades del pedido mediante métodos
def agregar_producto(self, producto, cantidad): # método que añade un producto y una cantidad
if not isinstance(producto, Producto): # si el producto no es instancia de la clase Producto
raise Exception('agregar_producto: el producto debe ser de la clase Producto')
if not isinstance(cantidad, int): # si la cantidad no es instancia de la clase int (número entero)
raise Exception('agregar_producto: cantidad debe ser un número entero')
if cantidad<=0:
raise Exception('agregar_producto: cantidad debe ser mayor que cero')
if producto in self.__productos: # si ya existe el producto
indice = self.__productos.index(producto) # tomamos el índice que ocupa
self.__cantidades[indice] += cantidad # aumentamos la cantidad previa
else: # si no existe es pq el producto es nuevo
self.__productos.append(producto)
self.__cantidades.append(cantidad)
def eliminar_producto(self, producto):
if not isinstance(producto, Producto): # si el producto no es instancia de la clase Producto
raise Exception('eliminar_producto: el producto debe ser de la clase Producto')
if producto in self.__productos: # si ya existe el producto
indice = self.__productos.index(producto) # tomamos el índice que ocupa
del self.__productos[indice]
del self.__cantidades[indice]
else:
raise Exception('eliminar_producto: el producto no existe')
def total_pedido(self):
total = 0
for (p,c) in zip(self.__productos, self.__cantidades):
total += p.calcular_total(c)
return total
def mostrar_pedido(self): # método con un print diferente al anteriro mostrar_pedido
for (p,c) in zip(self.__productos, self.__cantidades):
print(f"{p.nombre} → {c} unidades → {p.precio} €") # ahora usamos p.nombre y p.precio en lugar de todo el p
p1 = Producto(1, 'Producto 1', 5)
p2 = Producto(2, 'Producto 2', 10)
p3 = Producto(3, 'Producto 3', 20)
print(p1)
print(p2)
print(p3)
Código: 1, Nombre: Producto 1, Precio: 5 Código: 2, Nombre: Producto 2, Precio: 10 Código: 3, Nombre: Producto 3, Precio: 20
pedido = Pedido() # ahora el pedido se crea sin pasar parámentros
try: # trabajamos dentro de un try para recoger excepciones
pedido.agregar_producto(p1, 5) # agregamos productos al pedido p1
pedido.agregar_producto(p2, 5) # agregamos productos al pedido p2
pedido.agregar_producto(p3, 5) # agregamos productos al pedido p3
pedido.mostrar_pedido() # lanzamos el método mostrar_pedido
print('\tTotal pedido: ' + str(pedido.total_pedido())) # mostramos el total del pedido
print("="*40)
pedido.eliminar_producto(p1) # eliminamos el producto p1
pedido.mostrar_pedido() # mostramos nuevamente el pedido ahora sin p1
print('\tTotal pedido: ' + str(pedido.total_pedido())) # mostramos el total del pedido ahora sin p1
except Exception as e: # recopgemos las excepciones
print(e) # imprimimos las excepciones
Producto 1 → 5 unidades → 5 € Producto 2 → 5 unidades → 10 € Producto 3 → 5 unidades → 20 € Total pedido: 175 ======================================== Producto 2 → 5 unidades → 10 € Producto 3 → 5 unidades → 20 € Total pedido: 150
Veamos como saltan las excepciones.
pedido = Pedido()
try:
pedido.agregar_producto(p1, -5) # cantidad negativa, debe ser positiva
pedido.agregar_producto(p2, 5)
pedido.agregar_producto(p3, 5)
pedido.mostrar_pedido()
print('\tTotal pedido: ' + str(pedido.total_pedido()))
print("="*40)
pedido.eliminar_producto(p1)
pedido.mostrar_pedido()
print('\tTotal pedido: ' + str(pedido.total_pedido()))
except Exception as e:
print(e)
agregar_producto: cantidad debe ser mayor que cero
pedido = Pedido()
try:
pedido.agregar_producto(p1, "cinco") # una cadena en lugar de un número
pedido.agregar_producto(p2, 5)
pedido.agregar_producto(p3, 5)
pedido.mostrar_pedido()
print('\tTotal pedido: ' + str(pedido.total_pedido()))
print("="*40)
pedido.eliminar_producto(p1)
pedido.mostrar_pedido()
print('\tTotal pedido: ' + str(pedido.total_pedido()))
except Exception as e:
print(e)
agregar_producto: cantidad debe ser un número entero
pedido = Pedido()
try:
pedido.agregar_producto("tiritas", 5) # debe ser una instancia válida de producto
pedido.agregar_producto(p2, 5)
pedido.agregar_producto(p3, 5)
pedido.mostrar_pedido()
print('\tTotal pedido: ' + str(pedido.total_pedido()))
print("="*40)
pedido.eliminar_producto(p1)
pedido.mostrar_pedido()
print('\tTotal pedido: ' + str(pedido.total_pedido()))
except Exception as e:
print(e)
agregar_producto: el producto debe ser de la clase Producto
TIPO_DTO_FIJO = "Fijo" # constantes globales, el Dto. Fijo se refiere a dto. en euros
TIPO_DTO_PORC = "Porcentaje"
class Descuento: # creamos la clase Descuento
def __init__(self, tipo, valor):
if not isinstance(tipo, str): # en todos estos if tenemos que validar muchos aspectos
raise ValueError('constructor descuento: tipo debe ser una cadena')
if not isinstance(valor, int):
raise ValueError('constructor descuento: valor debe ser un número')
if tipo != TIPO_DTO_FIJO and tipo != TIPO_DTO_PORC:
raise ValueError('constructor descuento: el tipo debe ser fijo o porcentaje')
if tipo == TIPO_DTO_FIJO and valor <= 0:
raise ValueError('constructor descuento: el valor en el tipo fijo debe ser > 0')
if tipo == TIPO_DTO_PORC and (valor<=0 or valor>100):
raise ValueError('constructor descuento: el valor del porcentaje debe estar en el rango (0,100]')
self.__tipo = tipo
self.__valor = valor
@property # estas tres líneas son un Getter
def tipo(self):
return self.__tipo
@tipo.setter # estas tres líneas son un Setter
def tipo(self, v): # uso la v para recoger el parámetro y no confundir con valor
self.__tipo = v
@property # estas tres líneas son un Getter
def valor(self):
return self.__valor
@valor.setter # estas tres líneas son un Setter
def valor(self, v): # uso la v para recoger el parámetro y no confundir con valor
self.__valor = v
def aplicar_descuento(self, precio):
if self.__tipo == TIPO_DTO_FIJO:
if precio > self.__valor:
return precio - self.__valor
else:
return 0 # el precio no puede quedar negativo
else:
return precio - (precio * (self.__valor / 100))
#return precio * (1-self.__valor/100) # aplicamos el descuento en porcentaje
class Producto:
def __init__(self, codigo, nombre, precio, descuento = None): # agregamos el descuento como un parámetro opcional
self.__codigo = codigo
self.__nombre = nombre
self.__precio = precio
self.__descuento = descuento # añadimos el atributo __descuento
# Los Setter y Getter son iguales que en la clase Producto de la Primera parte salvo el Getter del precio
# No añadimos Setter y Getter para el atributo __descuento porque no se usará desde fuera
@property # Getter para el código
def codigo(self):
return self.__codigo
@codigo.setter # Setter para el codigo
def codigo(self, valor):
self.__codigo = valor
@property # Getter para el nombre
def nombre(self):
return self.__nombre
@nombre.setter # Setter para el nombre
def nombre(self, valor):
self.__nombre = valor
@property # Getter para el precio
def precio(self):
if self.__descuento == None: # si no hay descuento
return self.__precio # se envía el precio
else: # en caso contrario, cuando hay descuento
return self.__descuento.aplicar_descuento(self.__precio) # I M P O R T A N T E
@precio.setter # Setter para el precio
def precio(self, valor):
self.__precio = valor
def calcular_total(self, unidades):
return self.precio * unidades # IMPORTANTE: tenemos que poner precio y no __precio
def __str__(self):
return f"Código: {self.__codigo}, Nombre: {self.__nombre}, Precio: {self.__precio}"
class Pedido: # la clase Pedido no varía
def __init__(self):
self.__productos = []
self.__cantidades = []
def agregar_producto(self, producto, cantidad):
if not isinstance(producto, Producto):
raise Exception('agregar_producto: el producto debe ser de la clase Producto')
if not isinstance(cantidad, int):
raise Exception('agregar_producto: cantidad debe ser un número entero')
if cantidad<=0:
raise Exception('agregar_producto: cantidad debe ser mayor que cero')
if producto in self.__productos:
indice = self.__productos.index(producto)
self.__cantidades[indice] += cantidad
else:
self.__productos.append(producto)
self.__cantidades.append(cantidad)
def eliminar_producto(self, producto):
if not isinstance(producto, Producto):
raise Exception('eliminar_producto: el producto debe ser de la clase Producto')
if producto in self.__productos:
indice = self.__productos.index(producto)
del self.__productos[indice]
del self.__cantidades[indice]
else:
raise Exception('eliminar_producto: el producto no existe')
def total_pedido(self):
total = 0
for (p,c) in zip(self.__productos, self.__cantidades):
total += p.calcular_total(c)
return total
def mostrar_pedido(self):
for (p,c) in zip(self.__productos, self.__cantidades):
#print(f"{p.nombre} → {c} unidades → {p.precio} €")
print("Producto → (",p,"), Cantidad → " + str(c))
dto1 = Descuento(TIPO_DTO_FIJO, 2)
dto2 = Descuento(TIPO_DTO_PORC, 50)
p1 = Producto(1, 'Producto 1', 5)
p2 = Producto(2, 'Producto 2', 10, dto1)
p3 = Producto(3, 'Producto 3', 20, dto2)
print(p1)
print(p2)
print(p3)
Código: 1, Nombre: Producto 1, Precio: 5 Código: 2, Nombre: Producto 2, Precio: 10 Código: 3, Nombre: Producto 3, Precio: 20
pedido = Pedido()
try:
pedido.agregar_producto(p1, 5)
pedido.agregar_producto(p2, 5)
pedido.agregar_producto(p3, 5)
pedido.mostrar_pedido()
print('\tTotal pedido: ' + str(pedido.total_pedido()))
except Exception as e:
print(e)
Producto → ( Código: 1, Nombre: Producto 1, Precio: 5 ), Cantidad → 5 Producto → ( Código: 2, Nombre: Producto 2, Precio: 10 ), Cantidad → 5 Producto → ( Código: 3, Nombre: Producto 3, Precio: 20 ), Cantidad → 5 Total pedido: 115.0
# Comprobación: precio * cantidad + precio * cantidad + precio * cantidad
print("Importe del Pedido sin descuentos:", 5*5 + 10*5 + 20 * 5)
print("Importe del Pedido con descuentos:", 5*5 + 8*5 + 10 * 5)
Importe del Pedido sin descuentos: 175 Importe del Pedido con descuentos: 115
print("Precio del producto p1 con descuento:", p1.precio)
print("Precio del producto p2 con descuento:", p2.precio)
print("Precio del producto p3 con descuento:", p3.precio)
Precio del producto p1 con descuento: 5 Precio del producto p2 con descuento: 8 Precio del producto p3 con descuento: 10.0