Python第十四次课堂作业(类和对象):商品类增加购买功能

题目

在上次作业的基础上增加购买等交互

代码

# 商品类
# 用户类(父类)
# 普通用户类(子类)
# VIP用户类(子类)
# 管理员类(子类)
# 商家类
# 商品类
# 异常信息类
# 自定义装饰器类
import random
import time
from abc import ABCMeta, abstractmethod
from functools import wraps


# 错误信息类,用来规范化raise抛出的的错误信息
class ErrorType:
    def __init__(self, code, msg):
        self.code = code
        self.msg = msg

    @staticmethod
    def ParamsError(errMsg):
        return ErrorType(1001, "参数错误:" + errMsg)

    @staticmethod
    def LoginError(errMsg):
        return ErrorType(1002, "登录失败:" + errMsg)

    @staticmethod
    def NotLoginError():
        return ErrorType(1003, "你还未登录!无法进行操作!")

    @staticmethod
    def StockNotEnoughError():
        return ErrorType(1004, "库存不足!")

    @staticmethod
    def BalanceNotEnoughError():
        return ErrorType(1005, "余额不足!")

    @staticmethod
    def GoodsNotExistError():
        return ErrorType(1006, "商品不存在!")

    def __str__(self):
        return f"错误码:{self.code}, 错误信息 - {self.msg}"


# 自定义装饰器工具类
class CustomDecoratorUtils:
    # 以下两个函数为自定义装饰器,在调用被装饰器修饰的方法时,会走这两个装饰器函数。
    # 用于对原本函数进行功能添加,例如在设置商品属性前,要进行登录验证,那么装饰器就用于登录验证
    @staticmethod
    def user_login(func):
        @wraps(func)
        def wrap(self, *args, **kwargs):
            """可多次试错的登录接口,设置重试次数,默认为3次"""
            if self.isLogin:
                # 已经登录过了,无需再次登录,直接调用目标函数
                return func(self, *args, **kwargs)

            raise ValueError(ErrorType.NotLoginError())

        return wrap

    @staticmethod
    def catch_errors(func):
        """装饰器:该函数用于在运行func函数时若报错,则捕获异常"""

        # 这里主要用于捕获当用户设置属性时,若属性值不符合要求,函数会抛出异常,被该装饰器捕获。
        @wraps(func)
        def wrapper(self, *args, **kwargs):
            try:
                func(self, *args, **kwargs)
                return True, None
            except ValueError as _e:
                print(" - 操作失败:", _e)
                return False, _e

        return wrapper


class Goods:
    __goods_id: str = "000"  # 商品编号

    def __init__(self,
                 goods_name: str, purchase_price: float,
                 sale_price: float, discount: float, stock: int):
        # 这里设置goods_id时,使用self.id_number()方法,使用@classmethod装饰器。
        # 这样每次创建对象时,都会自动调用该方法,在上一个创建对象的id的基础上递增id:001 002 003
        self.__goods_id = self.id_number()  # 递增商品编号

        # 以下赋值操作,表面上是直接赋值,其实底层已经通过@xxx.setter装饰器,调用了对应的赋值方法
        # 这样做的好处是,可以在赋值时,进行一些额外的操作,比如传入的参数是否合法。
        self.__goods_name = goods_name  # 商品名称
        self.__purchase_price = purchase_price  # 进货单价 单位:元
        self.__sale_price = sale_price  # 销售单价 单位:元
        self.__discount = discount  # 销售折扣 0.0~1.0
        self.__stock = stock  # 库存 单位:件

    # 使用 @classmethod 装饰器,对goods_id进行递增,有记忆性
    @classmethod
    def id_number(cls):
        # 向前补0,补三位,比如:001 002 003
        cls.__goods_id = str(int(cls.__goods_id) + 1).zfill(3)
        return cls.__goods_id

    @property
    def goods_id(self):
        return self.__goods_id

    @property
    def goods_name(self):
        return self.__goods_name

    # 通过 @xxx.setter 装饰器,可以将赋值操作变成方法进行调用。类似于修改底层的赋值操作。
    @goods_name.setter
    def goods_name(self, goods_name: str):
        """修改商品名称,需要管理员验证"""
        # 商品名称不能为None和空
        if goods_name is None or goods_name == "":
            raise ValueError(ErrorType.ParamsError("商品名称不能为空"))
        self.__goods_name = goods_name

    @property
    def purchase_price(self):
        return self.__purchase_price

    @purchase_price.setter
    def purchase_price(self, purchase_price: float):
        """修改进货价,需要管理员验证"""
        # 进货价不能为负数
        if purchase_price < 0:
            raise ValueError(ErrorType.ParamsError("进货价不能为负数"))
        self.__purchase_price = purchase_price

    @property
    def sale_price(self):
        return self.__sale_price

    @sale_price.setter
    def sale_price(self, sale_price: float):
        """修改单价,需要管理员验证"""
        # 单价不能为负数
        if sale_price < 0:
            raise ValueError(ErrorType.ParamsError("单价不能为负数"))
        self.__sale_price = sale_price

    @property
    def discount(self):
        return self.__discount

    @discount.setter
    def discount(self, discount: float):
        """修改折扣,需要管理员验证"""
        # 折扣范围 0.0~1.0
        if 0.0 <= discount <= 1.0:
            self.__discount = discount
        else:
            raise ValueError(ErrorType.ParamsError("折扣范围 0.0~1.0"))

    @property
    def stock(self):
        return self.__stock

    @stock.setter
    def stock(self, stock: int):
        """修改库存,需要管理员验证"""
        # 库存不能为负数
        if stock < 0:
            raise ValueError(ErrorType.ParamsError("库存不能为负数"))
        self.__stock = stock


# 定义抽象类:用户类(抽象类的一个特点是它不能直接被实例化)
# 抽象类的目的就是让别的类继承它并实现特定的抽象方法
class User(metaclass=ABCMeta):
    __username: str  # 用户名
    __password: str  # 密码
    __isLogin: bool  # 是否登录

    def __init__(self, username, password):
        self.__username = username
        self.__password = password
        self.__isLogin = False

    def login(self, username: str, password: str) -> bool:
        """身份验证"""
        if self.__username == username and self.__password == password:
            self.__isLogin = True
            return True
        else:
            # 登录失败就重置登录状态
            self.__isLogin = False
            return False

    def logout(self):
        """注销"""
        self.__isLogin = False

    # 抽象方法:打印用户信息
    @abstractmethod
    def show_user_info(self):
        pass

    def __str__(self):
        return self.show_user_info()

    @property
    def username(self):
        return self.__username

    @property
    def password(self):
        return self.__password

    @property
    def isLogin(self):
        return self.__isLogin

    @staticmethod
    def custom_login(userList, retry_times=3):
        print(" - 请登录!你有{}次机会.".format(retry_times))
        for i in range(retry_times):
            _username = input("请输入用户名:")
            _password = input("请输入密码:")
            for _user in userList:
                if _user.login(_username, _password):
                    if type(_user) == Admin:
                        print(" - 登录成功!欢迎回来~ 你的身份是 管理员!")
                    elif type(_user) == VipConsumer:
                        print(" - 登录成功!欢迎回来~ 你的身份是 VIP用户~")
                    else:
                        print(" - 登录成功!欢迎回来~ 你的身份是 普通用户~")
                    return _user

            print(" - 登录失败!你还有", retry_times - i - 1, "次机会!")

        print(" - 登录失败!账号不存在或密码错误!")
        return None


# 消费者类,比User类多了购买的行为和属性,还有背包和购买记录等。
class Consumer(User):
    def __init__(self, username: str, password: str, balance: float):
        super().__init__(username, password)
        self.__balance = balance
        self.__backpack = []
        self.__records = []

    @CustomDecoratorUtils.user_login
    @CustomDecoratorUtils.catch_errors
    def buy(self, goods: Goods, num: int) -> bool:
        # 判断库存是否充足
        if goods.stock < num:
            raise ValueError(ErrorType.StockNotEnoughError())
        total_pay = goods.sale_price * num
        # 判断余额是否充足
        if self.__balance < total_pay:
            raise ValueError(ErrorType.BalanceNotEnoughError())
        # 扣除余额
        self.__balance -= total_pay
        # 扣除库存
        goods.stock -= num
        # 添加到背包
        self.__backpack.append({
            "goods_name": goods.goods_name,
            "num": num
        })
        # 添加到购买记录
        self.__records.append({
            "goods_name": goods.goods_name,
            "num": num,
            "sale_price": goods.sale_price,
            "total_price": total_pay,
            "buy_time": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        })
        return True

    # 获取购买记录
    @CustomDecoratorUtils.user_login
    @CustomDecoratorUtils.catch_errors
    @property
    def records(self):
        return self.__records

    # 获取背包
    @CustomDecoratorUtils.user_login
    @CustomDecoratorUtils.catch_errors
    @property
    def backpack(self):
        return self.__backpack

    @property
    def balance(self):
        return self.__balance

    @property
    def username(self):
        return super().username

    def show_user_info(self):
        text = f"【普通消费者】\n用户名:{super().username}\n余额:{self.balance}\n背包:\n"
        for index in range(len(self.__backpack)):
            text += f"{index + 1}. {self.__backpack[index]['goods_name']} x {self.__backpack[index]['num']}\n"
        text += "购买记录:\n"
        for index in range(len(self.__records)):
            text += f"{index + 1}. {self.__records[index]['goods_name']} x {self.__records[index]['num']} " \
                    f"单价:{self.__records[index]['sale_price']}元 总价:{self.__records[index]['total_price']}元 " \
                    f"购买时间:{self.__records[index]['buy_time']}\n"
        return text


# 会员用户类:可以享受折扣
class VipConsumer(Consumer):
    def __init__(self, username: str, password: str, balance: float):
        super().__init__(username, password, balance)
        self.__balance = balance
        self.__backpack = []
        self.__records = []

    @CustomDecoratorUtils.user_login
    @CustomDecoratorUtils.catch_errors
    def buy(self, goods: Goods, num: int):
        # 判断库存是否充足
        if goods.stock < num:
            raise ValueError(ErrorType.StockNotEnoughError())
        total_pay = goods.sale_price * num * goods.discount
        # 判断余额是否充足
        if self.__balance < total_pay:
            raise ValueError(ErrorType.BalanceNotEnoughError())
        # 扣除余额
        self.__balance -= total_pay
        # 扣除库存
        goods.stock -= num
        # 添加到背包
        self.__backpack.append({
            "goods_name": goods.goods_name,
            "num": num
        })
        # 添加到购买记录
        self.__records.append({
            "goods_name": goods.goods_name,
            "num": num,
            "sale_price": goods.sale_price,
            "discount": goods.discount,
            "total_price": total_pay,
            "buy_time": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        })

    def show_user_info(self):
        text = f"【尊贵的VIP用户】\n用户名:{super().username}\n余额:{self.__balance}\n背包:\n"
        for index in range(len(self.__backpack)):
            text += f"{index + 1}. {self.__backpack[index]['goods_name']} x {self.__backpack[index]['num']}\n"
        text += "购买记录:\n"
        for index in range(len(self.__records)):
            record_item = self.__records[index]
            text += f"{index + 1}. {record_item['goods_name']} x {record_item['num']} " \
                    f"单价:{record_item['sale_price']}元 享受折扣:{record_item['sale_price']}折 " \
                    f"总价:{record_item['total_price']}元 购买时间:{record_item['buy_time']}\n"
        return text

    @property
    def username(self):
        return super().username


# 管理员类
class Admin(User):
    def __init__(self, username: str, password: str):
        super().__init__(username, password)

    def show_user_info(self):
        text = f"【管理员】\n用户名:{super().username}\n"

        text += "————————————————————————"

        return text


# 店铺类
class Shop:
    def __init__(self, shop_name, balance, admins, goods_list):
        self.__shop_name = shop_name
        self.__goods_list = goods_list
        self.__balance = balance  # 余额
        self.__admins = admins  # 管理员列表

    # 采集库存
    @CustomDecoratorUtils.catch_errors
    def purchase_stock(self, goods: Goods, num):
        # 从店铺商品中找到这个商品
        for index in range(len(self.__goods_list)):
            if self.__goods_list[index].goods_name == goods.goods_name:
                # 增加库存
                self.__goods_list[index].stock += num
                # 采购商品,扣除余额
                self.__balance -= goods.purchase_price * num
                return True

        raise ValueError(ErrorType.GoodsNotExistError())

    @property
    def shop_name(self):
        return self.__shop_name

    @shop_name.setter
    def shop_name(self, shop_name):
        self.__shop_name = shop_name

    @property
    def goods_list(self):
        return self.__goods_list

    @staticmethod
    def show_all_goods(shop_name: str, goods_list: list):
        print(f"———————— 欢迎来到【{shop_name}】 ————————")
        print("{0:<10}{1:<10}{2:<10}{3:<10}{4:<10}".format("商品编号", "商品名称", "销售单价",
                                                           "销售折扣", "库存"))
        for goodsManageItem in goods_list:
            # 左对齐,保留2位小数
            print(
                "{0:<13}{1:<12}{2:<12.2f}{3:<12.2f}{4:<12.2f}".format(
                    goodsManageItem.goods_id,
                    goodsManageItem.goods_name,
                    goodsManageItem.sale_price,
                    goodsManageItem.discount,
                    goodsManageItem.stock)
            )

        print("———————————————————————————————————")

    def show_shop_detail(self):
        text = f"【{self.__shop_name}】\n余额:{self.__balance}\n管理员:\n"
        for index in range(len(self.__admins)):
            text += f"{index + 1}. {self.__admins[index].username}\n"
        text += "商品列表:\n"
        for index in range(len(self.__goods_list)):
            goods_item = self.__goods_list[index]
            text += f"{index + 1}. {goods_item.goods_name} 单价:{goods_item.sale_price}元 " \
                    f"折扣:{goods_item.discount} 折 库存:{goods_item.stock}\n"
        return text

    def __str__(self):
        return self.show_shop_detail()


def getAction(actionList):
    global YourUser
    print(f" - {'' if YourUser is None else YourUser.username} 你的菜单如下:")
    for index in range(len(actionList)):
        print(f"{actionList[index]}")
    _action = input("请输入你想进行的操作序号(输入q则退出):")
    if _action == "":
        print(" - 您没有输入操作序号!")
        return -1

    if _action == "q":
        print(" - 退出程序!")
        return -2

    try:
        _action = int(_action)
    except ValueError:
        print(" - 您输入的操作序号不是数字!")
        return -1

    if _action not in range(1, len(actionList) + 1):
        print(" - 您输入的操作序号不在范围内!")
        return -1

    return _action


def menu_user(_action):
    global YourUser
    global userList
    global globalShop

    if _action == 1:
        _user = User.custom_login(userList)
        if _user is None:
            return False
        YourUser = _user
        return True
    elif _action == 2:
        if YourUser is None:
            print(" - 请先登录!")
            return False
        YourUser.logout()
        print(" - 退出登录成功!")
        return True
    elif _action == 3:
        if YourUser is None:
            print(" - 请先登录!")
            return False
        print(YourUser.show_user_info())
        return True


def menu_goods(_action):
    global YourUser
    global globalShop

    if YourUser is None:
        print(" - 请先登录!")
        return False
    if type(YourUser) == Admin:
        print(" - 管理员无法购买商品!")
        return False
    if _action == 1:
        Shop.show_all_goods(globalShop.shop_name, globalShop.goods_list)
        goods_id = input("请输入你要购买的商品编号(输入q则退出):")
        if goods_id == "":
            print(" - 您没有输入商品编号!")
            return False
        if goods_id == "q":
            return True

        for goods in globalShop.goods_list:
            if goods.goods_id == goods_id:
                _num = input("请输入你要购买数量:")
                try:
                    _num = int(_num)
                except ValueError:
                    print(" - 您输入的数量不是数字!")
                    return False
                YourUser.buy(goods, _num)
                return True
        print(" - 您输入的商品编号不存在!")
        return False
    elif _action == 2:
        # 打印购买记录
        print(YourUser.show_user_info())
        return True
    elif _action == 3:
        # 打印背包
        print(YourUser.show_user_info())
        return True


def menu_shop(_action):
    global YourUser
    global globalShop
    global warehouse

    if YourUser is None:
        print(" - 请先登录!")
        return False
    if type(YourUser) != Admin:
        print(" - 非管理员无法操作此菜单!")
        return False
    if _action == 1:
        print(" ------ 仓库如下 ------- ")
        print("{0:<10}{1:<10}{2:<10}{3:<10}{4:<10}".format("商品编号", "商品名称", "销售单价",
                                                           "销售折扣", "库存"))
        for warehouseItem in warehouse:
            # 左对齐,保留2位小数
            print(
                "{0:<13}{1:<12}{2:<12.2f}{3:<12.2f}{4:<12.2f}".format(
                    warehouseItem.goods_id,
                    warehouseItem.goods_name,
                    warehouseItem.sale_price,
                    warehouseItem.discount,
                    warehouseItem.stock)
            )
        print(" ----------------------")

        goods_id = input("请输入你要采购的商品编号(输入q则退出):")
        if goods_id == "":
            print(" - 您没有输入商品编号!")
            return False
        if goods_id == "q":
            return True

        for goods in warehouse:
            if goods.goods_id == goods_id:
                _num = input("请输入你要采购数量:")
                try:
                    _num = int(_num)
                except ValueError:
                    print(" - 您输入的数量不是数字!")
                    return False
                if globalShop.purchase_stock(goods, _num):
                    print(" - 采购成功!")
                return True
        print(" - 您输入的商品编号不存在!")
        return False
    elif _action == 2:
        # 修改店铺名称
        _shop_name = input("请输入你要修改的店铺名称(输入q则退出):")
        if _shop_name == "":
            print(" - 您没有输入店铺名称!")
            return False
        if _shop_name == "q":
            return True
        globalShop.shop_name = _shop_name
        print(" - 修改店铺名称成功!")
        return True
    elif _action == 3:
        # 打印店铺信息
        print(globalShop)
        return True


if __name__ == '__main__':
    # 进货仓库
    warehouse = [
        Goods(goods_name="苹果", purchase_price=5, sale_price=10, discount=0.8, stock=100),
        Goods(goods_name="香蕉", purchase_price=2, sale_price=5, discount=0.5, stock=100),
        Goods(goods_name="橘子", purchase_price=3, sale_price=6, discount=0.6, stock=100),
    ]
    # 创建消费者(多态)
    userList = [
        Admin(username="admin1", password="123456"),  # 店铺管理员1
        Admin(username="admin2", password="123456"),  # 店铺管理员2
        Consumer(username="user1", password="123456", balance=100),  # 普通消费者
        Consumer(username="user2", password="123456", balance=100),
        VipConsumer(username="vip", password="123456", balance=100),  # Vip消费者
    ]
    adminList = [userList[0], userList[1]]
    # 初始化商店,设置管理员进去
    globalShop = Shop(shop_name="海哥超市", balance=1000, admins=adminList, goods_list=warehouse)
    # 随便采购一些商品
    globalShop.purchase_stock(warehouse[0], random.randint(1, 20))
    globalShop.purchase_stock(warehouse[1], random.randint(1, 20))
    globalShop.purchase_stock(warehouse[2], random.randint(1, 20))

    globalShop.show_all_goods(globalShop.shop_name, globalShop.goods_list)

    menuFatherList = ["1. 账号管理", "2. 商品交互", "3. 商品管理", "4. 退出程序"]
    menuItemList = [
        [
            "1. 登录账号",
            "2. 退出登录",
            "3. 查看我的全部信息",
            "4. 返回上级菜单",
        ],
        [
            "1. 购买商品",
            "2. 查看我的购买记录",
            "3. 查看我的背包",
            "4. 返回上级菜单",
        ],
        [
            "1. 采购商品",
            "2. 修改店铺名称",
            "3. 查看店铺数据信息",
            "4. 返回上级菜单",
        ]
    ]
    YourUser = None

    while True:
        action_father = getAction(menuFatherList)
        if action_father == -1:
            continue
        if action_father == -2 or action_father == 4:
            break
        # 选择子菜单
        while True:
            action_item = getAction(menuItemList[action_father - 1])
            if action_item == -1:
                continue
            if action_item == -2 or action_item == 4:
                break

            if action_father == 1:
                menu_user(action_item)
            elif action_father == 2:
                menu_goods(action_item)
            elif action_father == 3:
                menu_shop(action_item)

··`
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值