day21-模块,包,目录规范
一、一个python文件有两种用途
1、被当成程序运行
2、被当做模块导入
二者的区别是什么?
impot导入模块在使用时必须加前缀"模块."
优点:肯定不会与当前名称空间中的名字冲突
缺点:加前缀显得麻烦
from ... import ...导入也发生了三件事
1、产一个模块的名称空间
2、运行foo.py将运行过程中产生的名字都丢到模块的名称空间去
3、在当前名称空间拿到一个名字,该名字与模块名称空间中的某一个内存地址
from...impot...导入模块在使用时不用加前缀
优点:代码更精简
缺点:容易与当前名称空间混淆
一行导入多个名字(不推荐)
from foo import x,get,change
*:导入模块中的所有名字
name='egon'
from foo import *
print(name)
from socket import *
了解:__all__ (这个不推荐使用,最好别用)
from foo import *
print(x)
print(get)
print(change)
起别名
from foo import get as g
print(g)
二、模块的搜索路径优先级
无论是import还是from...import在导入模块时都涉及到查找问题
优先级:
1、内存(内置模块)
2、硬盘:按照sys.path中存放的文件的顺序依次查找要导入的模块
import sys
值为一个列表,存放了一系列的对文件夹
其中第一个文件夹是当前执行文件所在的文件夹
print(sys.path) import foo # 内存中已经有foo了 foo.say() import time time.sleep(10) import foo foo.say()
了解:sys.modules查看已经加载到内存中的模块 import sys import foo # foo=模块的内存地址 del foo def func(): import foo # foo=模块的内存地址 func() # print('foo' in sys.modules) print(sys.modules) import sys 找foo.py就把foo.py的文件夹添加到环境变量中 ys.path.append(r'/Users/linhaifeng/PycharmProjects/s14/day21/aa') import foo foo.say() from foo import say
三、函数的类型提示(这个使用也比较少)
str int ('play','music') def register(name:str,age:int,hobbbies:tuple)->int: print(name) print(age) print(hobbbies) return 111 # register(1,'aaa',[1,]) res=register('egon',18,('play','music')) def register(name:str='egon',age:int=18,hobbbies:tuple=(1,2))->int: print(name) print(age) print(hobbbies) return 111 # register(1,'aaa',[1,]) # res=register('egon',18,('play','music')) res=register()
def register(name:"必须传入名字傻叉",age:1111111,hobbbies:"必须传入爱好元组")->"返回的是整型": print(name) print(age) print(hobbbies) return 111 # register(1,'aaa',[1,]) # res=register('egon',18,('play','music')) # res=register('egon',19,(1,2,3)) print(register.__annotations__)#这条命令能把前面对应的函数的提示打印输出到屏幕上
四、包的使用
import foo
foo.f4()
强调:
1.关于包相关的导入语句也分为import和from ... import ...
两种,但是无论哪种,无论在什么位置,在导入时都必须遵循一个原则:
凡是在导入时带点的,点的左边都必须是一个包,否则非法。
可以带有一连串的点,如import 顶级包.子包.子模块,但都必须遵循这个原则。但对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)。
例如:
from a.b.c.d.e.f import xxx
import a.b.c.d.e.f
其中a、b、c、d、e 都必须是包
2、包A和包B下有同名模块也不会冲突,如A.a与B.a来自俩个命名空间
3、import导入文件时,产生名称空间中的名字来源于文件,
import 包,产生的名称空间的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件
import foo
# print(foo.f1)
# print(foo.f2)
# print(foo.f3)
# print(foo.f4)
foo.f4()
from foo import *
print(f1)
print(f2)
print(f3)
print(f4)
五、目录规范 (我把小说阅读器相关代码放上去了,好方便下次记忆)
软件开发目录规范
- bin
- start --> 启动软件入口
''' 项目启动入口 ''' import os import sys # 将项目的根目录,添加到sys.path中 sys.path.append( os.path.dirname(os.path.dirname(__file__)) ) from core import src if __name__ == '__main__': src.run()
- conf
- settings.py 配置文件
- 比如存放一些固定的路径
''' 此处存放固定的配置信息 ''' import os # 获取项目根目录reader_sys根目录 BASE_PATH = os.path.dirname(os.path.dirname(__file__)) # 获取db目录路径 # /python相关/python_files/01 python班级/reader_sys # /python相关/python_files/01 python班级/reader_sys/db DB_PATH = os.path.join(BASE_PATH, 'db') # 获取db.txt的根目录 DB_TXT_PATH = os.path.join(DB_PATH, 'db.txt') # story_class.txt文件目录路径 STORY_PATH = os.path.join(DB_PATH, 'story_class.txt') # 小说存放目录 FICTION_DIR = os.path.join(DB_PATH, 'fictions') # 日志文件的路径 LOG_PATH = os.path.join(BASE_PATH, 'log', 'log.txt')
- core
- src 核心业务逻辑代码
''' 此处存放核心业务逻辑代码 ''' from db import db_hanlder from lib import common import time # login_user: 若能进入该函数,证明用户已登录,能获取当前用户名 login_user = None # 注册功能 def register(): print('注册功能执行中...') while True: username = input('请输入用户名 (输入q退出): ').strip() # if username == 'q': # break # 1) 先校验用户是否存在 # 涉及数据的操作: 调用查看数据功能 ---> select # 给select函数传入当前输入的用户名,用于判断该用户是否存在 # user_data ---> [user, pwd, bal] or None user_data = db_hanlder.select(username) # 2)若存在,让用户重新输入 if user_data: print('当前输入的用户已存在,请重新输入!') continue password = input('请输入密码: ').strip() re_password = input('请确认密码: ').strip() # 3)两次密码是否一致的校验 if password == re_password: # 4) 将当前用户数据写入文件中 db_hanlder.save(username, password) print(f'用户[{username}]注册成功!') break else: print('两次密码不一致,请重新输入!') # 登录功能 def login(): print('登录功能执行中...') while True: username = input('请输入用户名 (输入q退出): ').strip() # 1) 查看当前用户是否存在 [user, pwd, balance] or None user_data = db_hanlder.select(username) # 2)若不存在,让用户重新输入 if not user_data: print('当前输入的用户不存在,请重新输入!') continue password = input('请输入密码: ').strip() # 3)校验用户输入的密码是否与db.txt中的密码一致 if password == user_data[1]: # 4)用户登录后记录登录状态 global login_user login_user = username print(f'用户[{username}]登录成功!') break else: print('密码错误,登录失败!') # 充值功能 @common.login_auth def recharge(): print('充值功能执行中...') while True: # 1) 让用户输入充值金额 balance = input('请输入充值金额: ').strip() # 2)判断用户输入的金额是否是数字 if not balance.isdigit(): print('请输入数字!') continue balance = int(balance) # 100 # 3) 修改当前用户的金额 # 3.1) 获取当前用户的数据 user, pwd, bal = db_hanlder.select(login_user) # [user, pwd, bal] # 3.2) 先获取用户 "修改前" 的数据 old_data = f'{user}:{pwd}:{bal}' # 3.3) 修改当前用户金额,做加钱操作 bal = int(bal) bal += balance # 3.4) 拼接 "修改后" 的当前用户数据 # 此时bal的值已经被修改 # 'tank:123:0' ---> 'tank:123:100' new_data = f'{user}:{pwd}:{bal}' # 3.5)调用修改数据功能 # 'tank:123:0' ---> 'tank:123:100' db_hanlder.update(old_data, new_data) print(f'当前用户: [{login_user}] 充值金额 [{balance}元] 成功!') # ending: 做充值日志记录 now_time = time.strftime('%Y-%m-%d %X') log_data = f'时间: {now_time} 用户名: {login_user} 充值金额:{balance}' print(log_data) common.append_log(log_data) break # 小说阅读功能 @common.login_auth def reader(): ''' 1、写该功能之前,先将小说数据,存放在story_class.txt文件中 2、先将story_class.txt文件中数据,获取出来,解析成字典类型 :return: ''' # 获取story_class.txt文件中的字典数据 # story_dic ---> {小说数据字典} ---> '' story_dic = db_hanlder.get_all_story() # 判断story_class.txt文件中是否有小说数据 if not story_dic: print('没有小说,请联系管理员上传~~') return '''story_dic { "0":{ "0":["倚天屠狗记.txt",3], "1":["沙雕英雄转.txt",10] }, "1":{ "0":["令人羞耻的爱.txt",6], "1":["二狗的妻子与大草原的故事.txt",5] }, "2":{ "0":["矮跟落叶归根养猪护理传.txt",100], "1":["矮跟在猪圈雨后的小故事.txt",500] }, } ''' while True: # 1) 打印小说种类选择信息 print(''' === 欢迎来到阅读小说功能主页 === 0 玄幻武侠 1 都市爱情 2 高效养猪36技 ============ end ============ ''') # 2)让用户输入小说类型编号 choice1 = input('请输入小说类型编号:').strip() # 3)判断当前用户选择的编号是否存在 # 若输入有误则,重新输入 if choice1 not in story_dic: print('输入有误,请重新输入!!!') continue # 4)获取当前小说类型中的所有小说数据 fiction_dic = story_dic.get(choice1) # choice1 == '1' '''fiction_dic { "0":["令人羞耻的爱.txt",6], "1":["二狗的妻子与大草原的故事.txt",5] }, ''' # 5)打印当前类型的所有小说 for number, fiction_list in fiction_dic.items(): name, price = fiction_list print(f'小说编号: [{number}] 小说名字: [{name}] 小说价格: [{price}]') # 6)让用户选择选择需要购买的小说 while True: choice2 = input('请输入需要购买的小说编号: ').strip() if choice2 not in fiction_dic: print('输入有误,请重新输入!!!') continue # name, price = ["二狗的妻子与大草原的故事.txt",5] # name --> 小说名字 price ---> 小说单价 name, price = fiction_dic.get(choice2) # 7)让用户输入y选择是否购买商品 choice3 = input(f'当前选择的小说名为: [{name}],商品单价为: [{price}], 请输入y购买,或者退出程序:').strip() # 8)判断用户输入的是否是y if choice3 == 'y': # 9)校验当前用户的金额是否 >= 小说单价 # 9.1)获取当前用户金额 user, pwd, bal = db_hanlder.select(login_user) # 9.2)判断金额 # 当前用户金额 bal = int(bal) # 小说单价 price = int(price) if bal < price: print('穷鬼,请先充值~~~') break # 10 开始扣费 # 10.1)拼接用户修改前的数据 old_data = f'{user}:{pwd}:{bal}' # 10.2) 开始对用户金额做减钱操作 bal -= price # 10.)拼接用户修改后的数据 new_data = f'{user}:{pwd}:{bal}' db_hanlder.update(old_data, new_data) print('当前小说购买成功,自动打开小说阅读~~') # 11) 调用获取想说详细信息数据 fiction_data = db_hanlder.show_fiction_data(name) print( f''' ==== 当前小说数据如下 ==== {fiction_data} ''' ) # 12) 记录购买成功日志 now_time = time.strftime('%Y-%m-%d %X') log_data = f'时间: {now_time} 用户名: {login_user} 消费金额:{price}' print(log_data) common.append_log(log_data) break break # 函数字典 func_dic = { '0': register, '1': login, '2': recharge, '3': reader, } # 启动函数 def run(): ''' # 一:程序运行开始时显示 0 账号注册 1 账号登录 2 充值功能 3 阅读小说 ''' while True: print(''' ===== 小说阅读器欢迎您 ===== 0 账号注册 1 账号登录 2 充值功能 3 阅读小说 ========== end ========== ''') # choice --> '1' choice = input('请输入功能编号 (温馨提示[输入q退出]): ').strip() if choice == 'q': break # 判断用户输入的编号是否在函数字典中 if choice not in func_dic: print('当前编号有误,请重新输入!') continue # func_dic.get('1')() --> register函数对象 ---> register() func_dic.get(choice)()
- db ---> 用于存放数据文件与操作数据的代码文件
- db_file ---> db.txt ...
- db_handler.py ---> 操作数据的代码
''' 用于存放操作数据代码 ''' from conf import settings import os # 查看数据 def select(username): ''' - 接收用户输入的用户名 - 若该用户存在,则返回当前用户的所有数据 - 若不存在,则返回None :param username: :return: ''' with open(settings.DB_TXT_PATH, 'r', encoding='utf-8') as f: # 获取db.txt文件中的每一行数据 for line in f: # 在每一行中,判断接收过来的用户名是否存在db.txt文件中 if username in line: # 若用户存在,则在当前行中提取该用户的所有数据 # user_data ---> [user, pwd, bal] user_data = line.strip().split(':') # 将当前用户数据,返回给调用者 return user_data # 保存数据 def save(username, password, balance=0): ''' :param username: 注册用户名 :param password: 注册密码 :param balance: 注册用户初始金额设置为默认值 :return: ''' with open(settings.DB_TXT_PATH, 'a', encoding='utf-8') as f: f.write(f'{username}:{password}:{balance}\n') # 更新数据 def update(old_data, new_data): ''' 将旧数据old_data:'tank:123:0' 替换成new_data:---> 新数据'tank:123:100' :return: ''' # 1) 拼接新的文件路径 new_path = os.path.join( settings.DB_PATH, 'new.txt' ) # 2) 读取db.txt文件中数据进行修改,写入到新文件new.txt文件中,再更换为db.txt文件名 with open(settings.DB_TXT_PATH, 'r', encoding='utf-8') as r_f, \ open(new_path, 'w', encoding='utf-8') as w_f: # 2.1)新旧数据替换 # python终极奥秘: 尽量让代码更简洁 all_user_data = r_f.read() all_user_data = all_user_data.replace(old_data, new_data) # 2.2)将新数据写入新文件中 w_f.write(all_user_data) # 3) 文件名的修改 # os.remove() 可以不写 # 这一步db.txt会将原来的覆盖掉 os.rename(new_path, settings.DB_TXT_PATH) # 获取小说字典数据 def get_all_story(): with open(settings.STORY_PATH, 'r', encoding='utf-8') as f: story_dic = eval(f.read()) return story_dic # 查看单本小说 def show_fiction_data(fiction_name): # 获取小说路径 fiction_path = os.path.join( settings.FICTION_DIR, fiction_name ) # 打开文件,获取文件数据,并返回给用户展示 with open(fiction_path, 'r', encoding='utf-8') as f: fiction_data = f.read() return fiction_data
- lib
- common: 存放公共的功能
- 比如存放 装饰器
''' 此处存放公共功能 ''' from conf import settings # 登录认证装饰器 def login_auth(func): # 解决问题: 循环导入问题 from core import src def inner(*args, **kwargs): if src.login_user: res = func(*args, **kwargs) return res else: print('未登录,不允许使用特殊功能, 请先登录~~~') src.login() return inner # 记录日志,应该放在公共功能中 def append_log(log_data): # 写入日志数据 with open(settings.LOG_PATH, 'a', encoding='utf-8') as f: f.write(log_data + '\n')
- log
- log.txt 存放日志的文件
时间: 2020-03-28 12:45:12 用户名: tank 充值金额:10000 时间: 2020-03-28 12:45:27 用户名: tank 消费金额:100
readme.txt
开发软件的一个相关功能需要与后续开发完怎么使用基本都会写在这个文档里面。