今日内容概要
- 包
- ATM和购物车
![image]()
今日内容详细
包
# 什么是包?什么是模块?
一个py文件就是一个模块
包就是多个模块功能的结合体
# 如何创建包?包和文件夹的区别?
包下有一个__init__.py文件
而文件夹是没有的
"""
模块首次被导入发生了3件事?
1. 产生一个名称空间
2. 执行被导入的py文件,然后把执行的数据都丢到名称空间
3. 执行文件中产生一个变量指向这个名称空间
包首次被导入发生了3件事?
1. 产生一个包的名称空间
2. 执行被导入包下的__init__.py文件,然后把执行的数据都丢到包的名称空间
3. 执行文件中产生一个变量aaa指向这个包的名称空间
"""
![image]()
ATM项目讲解
1. 开发项目的模式:
# 瀑布模式
项目开发完成之后,一并提交测试 禅道
# 敏捷开发
开发一个功能,测试一个功能
ATM使用瀑布模式
1. 需求分析
产品经理
模拟实现一个ATM + 购物商城程序
'''
额度 15000或自定义
实现购物商城,买东西加入 购物车,调用信用卡接口结账
可以提现,手续费5%
支持多账户登录
支持账户间转账
记录每月日常消费流水
提供还款接口
ATM记录操作日志
提供管理接口,包括添加账户、用户额度,冻结账户等。。。
用户认证用装饰器
'''
2. 程序设计或者架构设计
# 需求转为功能
1.注册
2.登录
3.转账
4.提现
5.充值
6.查看余额
7.查看流水
8.加入购物车
9.查看购物车
10.退出登录
11.结束
# 软件开发目录规范
bin:
start.py启动文件
core:
主要逻辑
def login():
pass
...
conf(config):
settings.py
db:
db.py
lib(library):
common.py
log:
日志文件
log.log
interface:
接口
bank.py
user.py
shopping.py
readme.txt
requirements.txt
3. 分任务分模块开发
4. 测试
5. 上线
# 启动文件一般放在bin目录,或者项目根目录
![image]()
启动脚本(start.py)
from core import src
def run():
while True:
print('''
**********欢迎光临**********
1.注册
2.登录
3.转账
4.提现
5.充值
6.查看余额
7.查看流水
8.加入购物车
9.查看购物车
10.退出登录
11.结束
**********欢迎光临**********
''')
func_dict = {
'1': src.register,
'2': src.login,
'3': src.transfer,
'4': src.withdrawal,
'5': src.recharge,
'6': src.view_balance,
'7': src.view_flow,
'8': src.add_cart,
'9': src.view_cart,
'10': src.log_out
}
choice = input('请选择功能>>>: ').strip()
# 如果客户输入的不在选项中
if not choice:
print('输入不合法')
continue
if choice == '11':
print('欢迎下次再来!!!')
return
if choice not in func_dict:
print('请选择1-10之间的数字')
continue
# 正常输入
func_dict[choice]()
# 判断当前文件为执行文件才开始执行
if __name__ == '__main__':
run()
配置文件(settings.py)
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
# 导入日志模块
import os
import logging
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
'[%(levelname)s][%(message)s]' #其中name为getlogger指定的名字
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' # 111
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
LOG_DIR = os.path.join(BASE_DIR, 'log')
# 判断当前路径是否存在
if not os.path.isdir(LOG_DIR):
os.mkdir(LOG_DIR)
# 拼接日志文件路径
logfile_path = os.path.join(LOG_DIR, 'log.log')
# log配置字典
LOGGING_DIC = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': standard_format
},
# 'simple': { #
# 'format': simple_format #
# }, #
},
'filters': {}, # 过滤日志
'handlers': {
#打印到终端的日志
# 'console': {
# 'level': 'DEBUG',
# 'class': 'logging.StreamHandler', # 打印到屏幕
# 'formatter': 'simple'
# },
#打印到文件的日志,收集info及以上的日志
'default': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件
'formatter': 'standard',
'filename': logfile_path, # 日志文件
'maxBytes': 1024*1024*5, # 日志大小 5M
'backupCount': 5,
'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了
},
},
'loggers': {
#logging.getLogger(__name__)拿到的logger配置 空字符串作为键 能够兼容所有的日志
'': {
'handlers': ['default'], # ['default', 'console']这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
'level': 'DEBUG',
'propagate': True, # 向上(更高level的logger)传递
}, # 当键不存在的情况下 (key设为空字符串)默认都会使用该k:v配置
},
}
公共功能(common.py)
from core import src
# 登录验证的装饰器
def login_auth(func):
def inner(*args, **kwargs):
# 执行函数之前的代码
# 判断是不是登录
if src.user_data['is_login']:
return func(*args, **kwargs)
# 执行函数之后的代码
else:
src.login()
return inner
import logging
import logging.config
from conf import settings
# 使用配置字典
def get_logger(username):
logging.config.dictConfig(settings.LOGGING_DIC) # 自动加载字典中的配置
logger1 = logging.getLogger(username) # 功能名实际应用时自行更改
return logger1
![image]()
核心功能(src.py)
import json
import os
from conf import settings
from lib import common
user_data = {
'username': None,
'is_login': False
}
# 1.注册功能
def register():
print('开始进行注册>>> ')
# 如果user_dict['is_login']为True 说明已经登录了
if user_data['is_login']:
print('当前已经登录成功,无法进行注册')
return
while True:
# 先获取用户名 比对是否已经注册过
username = input('username>>>: ').strip()
"""
要先用获取到的用户名对比是否已经存在
每个用户信息都单独存一个.json文件
"""
'''验证用户名是否存在,其实验证的是用户文件路径是否存在'''
# 拼接文件名
# 到配置文件中拿path = os.path.dirname(os.path.dirname(__file__))
path = settings.BASE_DIR
# 把客户文件拼接到根目录下
path_file = os.path.join(path, 'db', '%s.json' % username)
# 判断文件名是否存在
if os.path.exists(path_file):
print('用户名已经存在请重新输入')
continue
# 若不存在文件路径则继续获取用户信息
password = input('password>>>: ').strip()
balance = int(input('balance>>>: ').strip())
user_dict = {'username': username, 'password': password, 'balance': balance, 'locked': False, 'flow': [],
'shopping_cart': []}
'''
哪些数据可以直接写入文件?
1. 字符串
2. 二进制
'''
# 写入路径中
with open(path_file, 'w', encoding='utf-8') as f:
json.dump(user_dict, f)
print('用户 %s 注册成功咯' % username)
logger1 = common.get_logger('注册功能') # 调用日志
# 写入日志
logger1.debug('%s 注册成功' % username)
break
# 2.登录功能
def login():
# 如果user_dict['is_login']为True 说明已经登录了
if user_data['is_login']:
print('当前已经登录成功,无法再进行登录')
return
print('开始进行登录>>> ')
count = 0
while True:
username = input('username>>>: ').strip()
# 还是要先判断是否存在用户信息
path = settings.BASE_DIR
path_file = os.path.join(path, 'db', '%s.json' % username)
# 如果没有该用户文件
if not os.path.exists(path_file):
print('该用户目前尚未注册,请先进行注册')
return
# 否则存在用户文件 则继续获取用户密码
password = input('password>>>: ').strip()
# 读取用户信息进行比对
with open(path_file, 'r', encoding='utf-8') as f:
res = json.load(f)
if password == res.get('password'):
print('用户 %s 登录成功' % username)
logger1 = common.get_logger('登录功能') # 调用日志
logger1.debug('%s 登录成功' % username)
# 更改判断用户是否已经登录过的字典 用于全局认证
user_data['username'] = username
user_data['is_login'] = True
break
else:
print('用户名或密码错误')
# 输错三次锁定账户登录
count += 1
if count == 3:
res['locked'] = True
with open(path_file, 'w', encoding='utf-8') as f1:
json.dump(res, f1)
# 3.转账功能
@common.login_auth
def transfer():
print('开始进行转账')
while True:
username = input('请输入收款方姓名(q to sign out)>>>: ').strip()
# 还是要先判断是否存在用户信息
path = settings.BASE_DIR
path_file = os.path.join(path, 'db', '%s.json' % username)
if username == 'q':
return
elif username == user_data['username']:
print('付款账户和收款账户不能为同一人')
continue
# 如果没有该用户文件
elif not os.path.exists(path_file):
print('收款用户不存在,请输入其他用户')
continue
elif not username:
print('输入不合法,请重新输入>>>')
continue
while True:
# 收款用户存在 获取转账金额
money = (input('请输入转账金额>>>: ').strip())
# 先判断付款账户余额充足
path_file2 = os.path.join(path, 'db', '%s.json' % user_data['username'])
with open(path_file2, 'r', encoding='utf-8') as f2:
res2 = json.load(f2)
# 如果输入的不是纯数字
if not money.isdigit():
print('输入金额不合法,请重新输入')
continue
elif money <= '0':
print('输入金额不合法,请重新输入')
continue
elif not money:
continue
# 如果付款账户余额充足
elif res2['balance'] >= int(money):
# 将获取的用户输入转成整型存入文本
money = int(money)
# 读出收款用户文件内容
with open(path_file, 'r', encoding='utf-8') as f:
res = json.load(f)
if username == res['username']:
res['balance'] += money
res['flow'].append('%s 收到一笔来自于 %s 的汇款 %s¥' % (username, user_data['username'], money))
# 将收款方收到的钱存入收款账户文本中
with open(path_file, 'w', encoding='utf-8') as f1:
json.dump(res, f1, ensure_ascii=False)
logger1 = common.get_logger('转账功能') # 调用日志
logger1.debug('%s 收到一笔来自于 %s 的汇款 %s¥' % (username, user_data['username'], money))
# 付款账户内要减去转出的金额
# path = settings.BASE_DIR
# path_file2 = os.path.join(path, 'db', '%s.json' % user_data['username'])
# with open(path_file2, 'r', encoding='utf-8') as f2:
# res2 = json.load(f2)
# 将付款账户的金额减去已经转出的 并写入付款用户文本
res2['balance'] -= money
res2['flow'].append(
'%s 向用户 %s 汇出了 %s¥, 余额剩余>>>: %s¥' % (user_data['username'], username, money, res2['balance']))
with open(path_file2, 'w', encoding='utf-8') as f3:
json.dump(res2, f3, ensure_ascii=False)
print('转账成功\n你的账户剩余 %s¥' % res2['balance'])
logger1 = common.get_logger('转账功能') # 调用日志
logger1.debug('%s 向用户 %s 汇出了 %s¥, 余额剩余>>>: %s¥' % (
user_data['username'], username, money, res2['balance']))
return
else:
print('付款账户余额不足,穷逼一个还想转账')
# 4.提现功能
@common.login_auth # 还是先要判断用户是否已经登录
def withdrawal():
print('开始进行提现>>> ')
while True:
money = input('输入提现金额>>>: ')
# 验证输入情况
# 如果输入为空
if not money: continue
# 如果输入的不是纯数字
if not money.isdigit():
print('输入金额不合法')
continue
# 用户输入的是字符串 无法进行加减 所以先转为整型
money = int(money)
# 验证用户余额是否足够
# 先按照文件路径读取用户数据出来
path = settings.BASE_DIR
path_file = os.path.join(path, 'db', '%s.json' % user_data['username'])
with open(path_file, 'r', encoding='utf-8') as f:
res = json.load(f)
# 提现收取5%手续费 所以按照1.05倍对比
if res['balance'] >= money * 1.05:
# 减去用户提现的金额和手续费
res['balance'] -= money * 1.05
res['flow'].append('提现了 %s¥, 余额剩余 %s¥' % (money, res['balance']))
# 将剩余的余额写入用户文件内
with open(path_file, 'w', encoding='utf-8') as f1:
json.dump(res, f1, ensure_ascii=False)
print('提现成功')
print('当前账户余额为>>>: %s¥' % res['balance'])
logger1 = common.get_logger('提现功能') # 调用日志
logger1.debug('用户 %s 成功提现了 %s¥,当前账户余额为>>>: %s¥' % (user_data['username'], money, res['balance']))
return
else:
print('穷逼一个,没钱别来玩')
break
# 5.充值功能
@common.login_auth # 还是先要判断用户是否已经登录
def recharge():
print('开始进行充值')
while True:
recharge_amount = input('请输入充值金额>>>: ').strip()
# 验证输入情况
# 如果输入为空
if not recharge_amount:
print('输入金额不合法')
continue
# 如果输入的不是纯数字
elif not recharge_amount.isdigit():
print('输入金额不合法')
continue
recharge_amount = int(recharge_amount)
# 读取用户信息
path = settings.BASE_DIR
path_file = os.path.join(path, 'db', '%s.json' % user_data['username'])
with open(path_file, 'r', encoding='utf-8') as f:
res = json.load(f)
res['balance'] += recharge_amount
res['flow'].append('充值 %s¥ 成功,账户余额为>>>: %s¥' % (recharge_amount, res['balance']))
with open(path_file, 'w', encoding='utf-8') as f1:
json.dump(res, f1, ensure_ascii=False)
print('充值成功,当前账户余额为>>>: %s¥' % res['balance'])
logger1 = common.get_logger('充值功能') # 调用日志
logger1.debug('充值成功,当前账户余额为>>>: %s¥' % res['balance'])
return
# 6.查询余额
@common.login_auth # 还是先要判断用户是否已经登录
def view_balance():
print('开始查询余额')
path = settings.BASE_DIR
path_file = os.path.join(path, 'db', '%s.json' % user_data['username'])
with open(path_file, 'r', encoding='utf-8') as f:
res = json.load(f)
print('当前账户余额为>>>: %s¥' % res['balance'])
logger1 = common.get_logger('查询余额功能') # 调用日志
logger1.debug('用户 %s 查询了余额,当前账户余额为>>>: %s¥' % (user_data['username'], res['balance']))
return True
# 7.查询流水
@common.login_auth # 还是先要判断用户是否已经登录
def view_flow():
print('开始查询流水')
path = settings.BASE_DIR
path_file = os.path.join(path, 'db', '%s.json' % user_data['username'])
with open(path_file, 'r', encoding='utf-8') as f:
res = json.load(f)
res1 = res['flow']
for v in res1:
print(v)
logger1 = common.get_logger('查询流水功能') # 调用日志
logger1.debug('用户 %s 查询了账户流水' % user_data['username'])
return True
# 8.加入购物车
@common.login_auth
def add_cart():
print('开始浏览商品咯')
# 定义一个字典便于存放商品 {'商品名称':{'price':100, 'count':2}
shopping_cart = {}
while True:
# 先列出商品与价格
product_list = [
['iphone', 10000],
['mi', 1999],
['huawei', 5000],
['samsung', 7000]
]
# 用枚举的方法给列表中商品一一赋值
for k, v in enumerate(product_list, start=1):
print(k, v[0], v[1])
'''
结果:
1 iphone 10000
2 mi 1999
3 huawei 5000
4 samsung 7000
'''
# 让用户自己选择商品编号
number = input('请选择你喜欢的商品编号(q to sign out)>>>: ')
if number.isdigit():
number = int(number)
# print(len(product_list)) 4
# number = number - 1
if number > len(product_list):
print('没有该商品编号')
continue
trade_name = product_list[number-1][0]
trade_price = product_list[number-1][1]
# 验证商品是否存在
if trade_name not in shopping_cart:
# shopping_cart 实际为{'tea':{'price':goods_price, 'count':1},\
# 'coffee':{'price':goods_price, 'count':1}}
# 如果商品不在
shopping_cart[trade_name] = {'price': trade_price, 'count': 1}
else:
shopping_cart[trade_name]['count'] += 1
print('继续选择其他商品')
elif number == 'q':
# 保存商品信息到用户文本中
# 判断商品不能为空
if shopping_cart:
# 先读取用户文件内商品字典
path = settings.BASE_DIR
path_file = os.path.join(path, 'db', '%s.json' % user_data['username'])
with open(path_file, 'r', encoding='utf-8') as f:
user_dict = json.load(f)
# 创建商品字典
user_dict['shopping_cart'] = shopping_cart
with open(path_file, 'w', encoding='utf-8') as f1:
json.dump(user_dict, f1, ensure_ascii=False)
print('添加购物车成功')
logger1 = common.get_logger('添加购物车功能') # 调用日志
logger1.debug('用户 %s 添加了 %s 手机到购物车中' % (user_data['username'], trade_name))
break
else:
print('输入不合法')
# 9.查看购物车
@common.login_auth
def view_cart():
print('开始查询购物车')
while True:
path = settings.BASE_DIR
path_file = os.path.join(path, 'db', '%s.json' % user_data['username'])
with open(path_file, 'r', encoding='utf-8') as f:
user_dict = json.load(f)
print(user_dict['shopping_cart'])
logger1 = common.get_logger('查看购物车功能') # 调用日志
logger1.debug('用户 %s 查询了购物车' % user_data['username'])
break
# 10.退出登录
def log_out():
# 先判断是否登录 已经登录才可以退出
if user_data['is_login']:
choice = input('确认退出请按y,否则将返回主页').strip()
if choice == 'y':
# 用户确认退出 将全局标志位改为False即可
user_data['is_login'] = False
logger1 = common.get_logger('退出登录功能') # 调用日志
logger1.debug('用户 %s 退出了登录状态' % user_data['username'])
return
else:
return
else:
print('当前未登录,无需退出')
![image]()