前言
光阴似箭,日月如梭,不知不觉我们来到了Python基础语法课的最后一关。
恭喜你在这段学习生涯中,至少“喜提”上万行代码,赶超绝大多数同龄人!
今天是你的毕业礼,按照国际惯例,理应会有毕业寄语。不过告别的话总得最后说,我们还是先来完成我们的最后一个项目吧。
相信经历过前面几个项目实操的磨练,相信现在的你看到比较长的代码是淡定得很吧。
毕业项目背景是这样的:这个项目是朋友公司的一个小姐姐,在原本零基础的情况下学完Python基础语法课后帮朋友做出来的。
然后她跑来跟我“吹嘘”,因为这个小程序,朋友得到了老板额外给的一笔奖金,然后请了她大餐一顿。
我一看,哟~这小姑娘就是拿了我在课堂上教的东西去“班门弄斧”了嘛,但我却颇感欣慰,我写的课是实实在在能够帮助人啊(流下了老父亲的泪水)。
所谓赠人代码,手有余香,相信这个小姑娘会更加努力学Python的吧。😃
老规矩,项目三部曲,先来看这个项目目标是什么。
明确项目目标
因为这个程序应用的场景比较特殊,没有相关经验的同学可能不太理解,所以我会先花点功夫把我们要做的项目讲得清楚一些。(以下我会称小姑娘的朋友为A君)
A君目前在一家资产评估公司实习,他的工作日常就是跑遍市区里的住宅小区,调查小区的地址、建筑年份和每栋楼里每个单元里每一户的朝向和面积。
比如一户的资料是:富安花园(小区)3栋 2单元 401户;朝向:南北朝向;面积:90平方。他需要把每一户的信息都记录下来。
为了节省时间,通常他们会当场把一栋楼里所有单元的数据画在一张平面图里。等回去之后,再把平面图记录的数据,录入到excel表里。
举个例子,上面这张平面图表示的是这一栋有4个单元,每个单元有15层楼,其中1,2单元有两户,面积和朝向是140平方,南北朝向。
3单元有四户,有2户面积60平方,南北朝向,2户面积80平方,东西朝向;4单元有四户,面积70平方,东西朝向。
A君向小姑娘抱怨原本看房已经很累人了,录入更烦。可能你觉得用excel的自动填充功能不是很方便吗,事情远没有这么简单。
确实,有一些重复的单元格只要填一次,自动下拉就可以复制粘贴,对于A君(excel菜鸟)来说,每次只要手动输入户室号就可以。(当然掌握一些excel技巧可以节省一些功夫)
不过,不同单元的户数、朝向和面积不完全是一样的,所以不能以一个单元的数据复制给其他单元。
比如说,好不容易输入了1单元的数据,复制给了2单元,但因为3单元有4户,面积和朝向都不一样,所以3单元又得重新处理,户室号、面积、朝向都不一样。
好不容易搞完3单元,到了4单元,面积和朝向又不一样了。求此时A君心里的阴影面积。
再者,一栋楼通常就有两三百条数据,通常一个小区有多栋楼,每栋楼的构造也不太一样,如果要把一个片区的数据放在一个表里,可能就有好几千条数据。
在这让人有密集恐惧的数据面前,数据一多,就很容易出错。一旦出错,一是难以发现,二是改起来挺费劲,牵连甚广。如果你经常和excel打交道的话,我想你多少能感同身受。
这样经历了和excel斗智斗勇斗出个斗鸡眼的一段时间后,A君给小姑娘发了一个求助的微信:不是说Python能解决重复性劳动咩?你给我支支招呗!
于是小姑娘捣鼓了一番,做出来了这么一个程序:
只要在终端输入一些模版数据,程序就可以自动录入剩下的数据,甚至连excel都不用打开。
在终端运行该程序后,打开excel对应的文件,就会发现数据是按一定的规律录入好的。
所以,我们今天的任务就是把这个程序做出来。
当然你的工作可能不像A君这样要处理这样的事情,但我仍希望你能从中找到一些解决问题的共性,迁移到你或许能应用到的工作场景中。
分析过程,拆解项目
为了便于教学,今天我们的目的是希望把一个小区某一栋楼内的所有户室的资料录入到excel表里,这栋楼的资料是这样子的:
上一关我们学到了流程图,我们可以尝试将实现过程拆解成流程图的步骤。
小姑娘当时画了个简陋版的流程图:
可见,这里的关键点是找到数据中的规律,确定可以复用的模版数据,我们在模块那关也学到csv模块是可以处理excel文件格式的,所以老师将这一项目拆解成3个版本,也可以当成是三大步骤。
具体每一版本要做的工作,我们一个个来看吧。
逐步执行,代码实现
首先,我们需要了解我们需要什么数据,也就是表格里的表头,这些一般是业务规定的,没有什么商量的余地。
版本1.0: 输入表头,确定模版数据
当我们要在Python读写excel的时候,调用csv模块是个很好的选择。相信你对csv文件读写的知识还有印象吧,下面就请你根据提示补全代码,往csv写入表头。
参考答案:
import csv
#调用csv模块
with open('assets.csv', 'a', newline='') as csvfile:
#调用open()函数打开csv文件,传入参数:文件名“assets.csv”、追加模式“a”、newline=''。
writer = csv.writer(csvfile, dialect='excel')
# 用csv.writer()函数创建一个writer对象。
header=['小区名称', '地址', '建筑年份', '楼栋', '单元', '户室', '朝向', '面积']
writer.writerow(header)
#用writerow()函数将表头写进csv文件里
此时,如果你复制代码到本地编辑器运行,就会在py文件所在的文件夹里,看到新建了一个名为assets的csv文件,打开的话就是这样的:
这里稍微提一下,如果你用的是Mac电脑,当你直接用excel打开csv文件可能会显示乱码,这是因为Mac电脑打开excel是默认用GBK编码处理中文的,而Python默认是UTF-8编码,所以导致加载出来是乱码。
解决方案是Mac用户要在open()函数里加上参数’encoding=‘GBK’(如果选择用Numbers打开就不需要)
with open('assets.csv', 'a', newline='',encoding='GBK') as csvfile:
确定好表头之后,接下来,我们要确定以哪些数据为模版数据,也就是哪些数据只需输入一次便可重复利用,写入循环。
我们一个个来看,一个小区一栋楼的小区名称、地址、建筑年份、肯定是相同的,所以可以输入一次,复用所有。
title = input('请输入小区名称')
address = input('请输入小区地址:')
year = input('请输入小区建造年份:')
block = input('请输入楼栋号:')
而同一栋内部的单元分布也许是不同的,比如说有的单元只有两户室,有的单元有四户室,所以它的房间朝向、面积也会不同,所以一个单元的数据不能做模板数据。
我们再缩小范围,我们会发现在同一单元下,每一楼的户型和朝向一般是一样的,也就是只需要知道一个单元下某一楼层的数据,该单元的其它楼层都可以复用,区别只是户室号。
确定了以此为模版数据,下一阶段我们就来看看具体怎么转化成代码的形式。
阶段2:获取、复用模版数据
在阶段1,我们已经确定了模版数据是一个单元下某一层楼的数据,也就是说我们只要拿到起始楼层的每个户室的数据(朝向和面积),这个单元内其他楼层的户室都可以套用。
而户室号一般是由楼层+后两位序号(以下我会简称为尾号)组成的,如201,202,203,204;301,302,303,304…尾号是统一的,变的只是前面的楼层数(每层加一),所以户室号拆成两部分处理会方便我们后续的操作。
那么,接下来,我们就来获取模版数据吧~不过每次获取数据前我们就得想好我们要用什么样的形式存储数据。
现在我们需要拿到的是起始楼层每一个户室的朝向和面积,我们可以判断出朝向和面积是并列关系,它是从属于户室的。
我们很自然能想到这里用字典嵌套列表的形式会更方便,其中字典的键是户室号,面积和朝向组成的列表为字典的值,类似这种形式。
start_floor_rooms = {301:['南北',70],302:['南北',70],303:['东西',80],304:['东西',80]}
#初始楼层的数据
我们一步步来看,首先我们来把第一对键值对 301:[‘南北’,70] 放进字典start_floor_rooms里,这是我们之前反复接触过的,你可以先看代码和注释。
start_floor = input('请输入起始楼层:')
end_floor = input('请输入终止楼层:')
#确定每一单元有几层楼
start_floor_rooms = {}
#创建字典,存放起始楼层所有户室的信息
floor_last_number = []
#创建列表,存放户室的尾号如['01','02','03'],后续楼层可复用
last_number = input('请输入起始楼层户室的尾号:(如01,02)')
floor_last_number.append(last_number)
#将元素添加到存放户室尾号的列表里,如floor_last_number = ['01']
room_number = int(start_floor + last_number)
#户室名为room_number,由楼层start_floor和尾号last_number组成,如'301'
direction = int(input('请输入 %d 的朝向(南北朝向输入1,东西朝向输入2):' % room_number ))
#输入中文比输入数字要麻烦许多,我们可以先用1和2代替
area = int(input('请输入 %d 的面积,单位 ㎡ :' % room_number))
start_floor_rooms[room_number] = [direction,area]
# 户室号为键,朝向和面积组成的列表为值,添加到字典里,如start_floor_rooms = {301:[1,70]}
print(start_floor_rooms)
你可以运行一下,看看目前的代码的运行结果是什么。
运行结果:
请输入起始楼层:5
请输入起始楼层户室的尾号:(如01,02)16
请输入 516 的朝向(南北朝向输入1,东西朝向输入2):2
请输入 516 的面积,单位 ㎡ :68
{516: [2, 68]}
可见,这段代码只能让我们获取起始楼层一个户室的信息,为了获取起始楼层其他户室的信息,我们需要加入循环。
icon
现在请你在下面代码的基础上改进:【在已经缩进的代码块前加入循环语句】,实现打印效果{301:[1,70],302:[1,70],303:[2,60]}。
因为每一单元的户室数不是固定的,所以用while循环会更妥当,我们可以先定义一个循环控制量,让循环条件为True,再“人为”地打破循环,你可以看下参考代码:
start_floor = input('请输入起始楼层:')
end_floor = input('请输入终止楼层:')
input('接下来请依次输入起始层每个房间的户室尾号、南北朝向及面积,按任意键继续')
start_floor_rooms = {}
floor_last_number = []
# 收集起始层的房间信息
# 定义循环控制量
room_loop = True
while room_loop:
last_number = input('请输入起始楼层户室的尾号:(如01,02)')
floor_last_number.append(last_number)
#将尾号用append()添加列表里,如floor_last_number = ['01','02']
room_number = int(start_floor + last_number)
#户室号为room_number,由楼层start_floor和尾号last_number组成,如301
direction = int(input('请输入 %d 的朝向(南北朝向输入1,东西朝向输入2):' % room_number ))
area = int(input('请输入 %d 的面积,单位 ㎡ :' % room_number))
start_floor_rooms[room_number] = [direction,area]
# 户室号为键,朝向和面积组成的列表为值,添加到字典里,如start_floor_rooms = {301:[1,70]}
continued= input('是否需要输入下一个尾号?按 n 停止输入,按其他任意键继续:')
#加入打破循环的条件
if continued == 'n':
room_loop = False
else:
room_loop = True
print(start_floor_rooms)
那么现在,我们就获得了一个存储了某一单元起始楼层所有户室信息的字典,请你运行一下,看看是不是这样子。
运行结果:
请输入起始楼层:1
请输入终止楼层:3
接下来请依次输入起始层每个房间的户室尾号、南北朝向及面积,按任意键继续
请输入起始楼层户室的尾号:(如01,02)01
请输入 101 的朝向(南北朝向输入1,东西朝向输入2):1
请输入 101 的面积,单位 ㎡ :33
是否需要输入下一个尾号?按 n 停止输入,按其他任意键继续:n
{101: [1, 33]}
好的,现在我们就已经获得了起始楼层所有户室的数据,下一步我们就要将这层的数据迁移到其他楼层里,即只需把户室号的楼层数加一。
那怎么迁移呢?我们的第一反应可能是给每一个楼层都创建一个新字典,这样虽然可行,但随着楼层的增加,字典就会有非常多个,不好管理,
所以把一个单元内所有楼层的数据放在一块会更方便我们的处理,也就是说将每一个楼层的字典统一放在一个存放单元级的字典里,我们希望的格式应该是这样的:
star_floor = 3
end_floor = 5
#为了举例,假设这个单元只有3-5楼
start_floor_rooms = {301:[1,80],302:[1,80],303:[2,90],304:[2,90]}
#初始楼层的模版数据
unit_rooms={ 3:{301:[1,80],302:[1,80],303:[2,90],304:[2,90]},
4:{401:[1,80],402:[1,80],403:[2,90],404:[2,90]},
5:{501:[1,80],502:[1,80],503:[2,90],504:[2,90]}
}
#存放了该单元所有楼层数据的字典,键是楼层数,值是每一楼层所有户室的信息,也是一个字典。
虽然unit_rooms这个结构看起来很复杂,但我们分析一下就知道,这是字典嵌套字典的情况。(为了方便讲解,以下我会把最外层的字典称为大字典,里层的字典称为小字典)
大字典的键是楼层数,值是小字典,而每个小字典和初始楼层start_floor_rooms的数据只是户室号的区别。
那么接下来我们只需往unit_rooms不断添加键值对就可以了。为了化繁为简,展示重点,接下来我会给出一些固定变量,是我们上一阶段的代码能获取的结果。
还记得怎么往空字典添加键值对吧?现在请你补全下面代码,打印出unit_rooms,打印结果为{3:{301:[1,80],302:[1,80],303:[2,90]}}。
start_floor = '3'
start_floor_rooms = {301:[1,80],302:[1,80],303:[2,90]}
#初始楼层的数据
unit_rooms = {}
print(unit_rooms)
#创建一个存放单元所有楼层数据的字典
参考答案是这样的:
start_floor = '3'
start_floor_rooms = {301:[1,80],302:[1,80],303:[2,90]}
#初始楼层的数据
unit_rooms = {}
unit_rooms[start_floor]=start_floor_rooms
print(unit_rooms)
#创建一个存放单元所有楼层数据的字典
现在字典里已经有了3楼所有户室的信息,接下来就是往字典里添加其他楼层的信息(键值对)。
假设现在的单元只有3楼和4楼,每一楼层都有3个户室,我们来看看怎么把3楼的数据复制给4楼,并放在unit_rooms里。
你可以尝试一下,如果做不出来可以直接查看参考代码:
请你仔细阅读下列的代码,每一步的结果我都标明在注释里。
start_floor = '3'
end_floor = '4'
floor_last_number = ['01','02','03']
# 之前input()输入的数据为str类型
start_floor_rooms = {301:[1,80], 302:[1,80], 303:[2,90]}
#初始楼层的模版数据
unit_rooms = {}
#创建一个字典,存储所有楼层的数据
unit_rooms[int(start_floor)] = start_floor_rooms
#unit_rooms = {3: {301: [1, 80], 302: [1, 80], 303: [2, 90]}}
floor_rooms = {}
#给4楼创建一个字典
for i in range(len(start_floor_rooms)):
#遍历每层有多少个房间,这里是3,即执行for i in range 3 的循环
number = '4' + floor_last_number[i]
#字符串拼接, number = ['401','402','403']
info = start_floor_rooms[int(start_floor + floor_last_number[i])]
# 依次取出字典start_floor_rooms键对应的值,即面积和朝向组成的列表
# int(start_floor + floor_last_number[0])= 301
# info = [1,80]
floor_rooms[int(number)] = info
#给字典floor_rooms添加键值对,floor_rooms = {401:[1,80]}
#循环三次,所以floor_rooms = {401:[1,80], 402:[1,80], 403:[2,90]}
unit_rooms[4] = floor_rooms
#以4为键,floor_rooms为值,给字典unit_rooms添加键值对
print(unit_rooms)
#unit_rooms = {3: {301: [1, 80], 302: [1, 80], 303: [2, 90]}, 4: {401: [1, 80], 402: [1, 80], 403: [2, 90]}}
你可以运行一下,如果哪一步不清楚,可以先把后面的代码注释掉,print()出来看看是什么结果。
现在我们已经知道怎么往unit_rooms添加键值对了吧,不过一个单元一般不会只有两层楼,接下来我们就需要加入循环,将所有楼层的数据一次性放进字典unit_rooms里。
在一开始,我们可以通过input()询问知道一栋楼里有多少层。
start_floor = input('请输入起始楼层:')
end_floor = input('请输入终止楼层:')
现在我们假设一栋楼是3-7楼,即start_floor = 3,end_floor = 7。
因为楼层是已知的,那么这里用for循环会更方便一些,循环的执行次数应该是除初始楼层之外剩余的楼层数。(因为初始楼层是模版数据,需要手动录入)
也就是说如果是3-7楼,我们的循环是执行4、5、6、7四次,所以循环的条件就可以写成:
for floor in range(start_floor + 1, end_floor + 1):
# 在此例中,为 for floor in range (4,8)
那么,我们将循环加入上一步,结果就是:
start_floor = '3'
end_floor = '7'
floor_last_number = ['01','02','03']
start_floor_rooms = {301:[1,80],302:[1,80],303:[2,90]}
#初始楼层的模版数据
unit_rooms = {}
#新建一个存放所有楼层的字典
unit_rooms[int(start_floor)] = start_floor_rooms
for floor in range(int(start_floor) + 1, int(end_floor) + 1):
#遍历除初始楼层外的其他楼层
floor_rooms = {}
#每个楼层都建立一个字典
for i in range(len(start_floor_rooms)):
#遍历每层有多少个房间,这里是3,即执行for i in range 3 的循环
number = str(floor) + floor_last_number[i]
info = start_floor_rooms[int(start_floor + floor_last_number[i])]
# 依次取出字典start_floor_rooms键对应的值,即面积和朝向组成的列表
floor_rooms[int(number)] = info
#给字典floor_rooms添加键值对,floor_rooms = {401:[1,80]}
unit_rooms[floor] = floor_rooms
print(unit_rooms)
直接运行一下,看看效果吧~
很好,现在我们就把一个单元里所有楼层的数据放进了字典unit_rooms里了,接下来就是写入我们在一开始创建好的csv文件里。
阶段3.0:写入csv文件
在阶段2.0,我们把一个单元内所有户室的数据都存在unit_rooms这个字典里,理论上我们已经可以以单元为单位写入到csv文件,现在最关键的是如何从字典取出我们想要的数据。
我们来看一开始已经输入过的表头,它们是’小区名称’, ‘地址’, ‘建筑年份’, ‘楼栋’, ‘单元’, ‘户室’, ‘朝向’, ‘面积’
其中’小区名称’,‘地址’,‘建筑年份’, ‘楼栋’, '单元’都需要手动输入,并赋值给相应的变量,不过只需输入一遍就能复用到其余所有单元格里。
title = input('请输入小区名称')
address = input('请输入小区地址:')
year = input('请输入小区建造年份:')
block = input('请输入楼栋号:')
unit = input('请输入单元号:')
剩下的’户室’、'朝向’和’面积‘都存储在字典unit_rooms里,假设现在字典是长这样的:
unit_rooms={ 3:{301:[1,80],302:[1,80],303:[2,90],304:[2,90]},
4:{401:[1,80],402:[1,80],403:[2,90],404:[2,90]},
5:{501:[1,80],502:[1,80],503:[2,90],504:[2,90]}
}
我们来思考一下要怎么一次性把户室号(301-304,401-404,501-504)和户室号对应的朝向和面积都一次性取出来呢?
相信你会有点想法,又有点不太确定,那么就请你打代码试试看吧~
咋样,成功了吗?下面来讲讲我的思路。
同样我们把最外层的字典unit_rooms成为大字典,里面嵌套的字典成为小字典(sub_dict)。
可见,大字典的值是小字典(sub_dict),其中户室是小字典的键,朝向是小字典的值(列表)的第0个元素,面积是小字典的值(列表)的第1个元素。
也就是说我们需要的数据都在大字典的值里,所以第一步我们需要遍历大字典里的键(key),来取出值(value)。
有些同学可能会这么写:
#阅读代码,直接运行即可
unit_rooms={ 3:{301:[1,80],302:[1,80],303:[2,90],304:[2,90]},
4:{401:[1,80],402:[1,80],403:[2,90],404:[2,90]},
5:{501:[1,80],502:[1,80],503:[2,90],504:[2,90]}
}
for i in range(3,6):
sub_dict = unit_rooms[i]
print(sub_dict)
这当然没问题,不过我在这里想给大家介绍一种新方法,可以直接遍历字典里所有的值,这样就不用去数字典里有多少个键值对了。它的语法是:
for value in DictName.values():
# value的名字可以自行另取
# DictName是要遍历的字典的名称
# .values():是固定的用法
所以,如果我们要取出unit_rooms的值,就可以写成:
# 直接运行即可
unit_rooms = {3:{301:[1,80],302:[1,80],303:[2,90],304:[2,90]},
4:{401:[1,80],402:[1,80],403:[2,90],404:[2,90]},
5:{501:[1,80],502:[1,80],503:[2,90],504:[2,90]}
}
for sub_dict in unit_rooms.values():
print(sub_dict)
好,现在我们已经取出小字典(sub_dict)了,前面我们分析过户室号是sub_dict的键,朝向和面积是sub_dict的值,所以下一步就是要遍历sub_dict的键和值,这里我也介绍一种新的语法。
for k,v in DictName.items():
#遍历字典的键值对,k对应键,v对应值
#k,v 的名字可以自己取,DictName是字典名
举个例子,你可以运行以下代码看看结果。
tv_dict = {'芒果台':'湖南卫视', '荔枝台':'江苏卫视', '番茄台':'东方卫视'}
for logo,name in tv_dict.items():
print(logo + '是' + name)
运行结果:
芒果台是湖南卫视
荔枝台是江苏卫视
番茄台是东方卫视
回到正题,现在请你在下列代码的基础下,补全代码,打印出字典下每一户室的门牌号、朝向和面积,格式为:【户室号:301 朝向:1 面积:80 】
unit_rooms = {3:{301:[1,80],302:[1,80],303:[2,90],304:[2,90]},
4:{401:[1,80],402:[1,80],403:[2,90],404:[2,90]},
5:{501:[1,80],502:[1,80],503:[2,90],504:[2,90]}
}
for sub_dict in unit_rooms.values():
# 遍历大字典的值,即小字典sub_dict
参考答案:
unit_rooms = {3:{301:[1,80],302:[1,80],303:[2,90],304:[2,90]},
4:{401:[1,80],402:[1,80],403:[2,90],404:[2,90]},
5:{501:[1,80],502:[1,80],503:[2,90],504:[2,90]}
}
for sub_dict in unit_rooms.values():
for room,info in sub_dict.items():
#room对应的是键,即户室号;info对应的是sub_dict的值,是一个列表
print('户室号:%d 朝向 %d 面积:%d' % (room,info[0],info[1]))
#info[0]对应的是朝向,info[1]对应的是面积
现在还有个细节是一开始我们将’南北’记成1,'东西’记成2,所以现在我们需要改回来,当然你可以写入csv文件再全部替换,不过我们还是有始有终,在Python完成操作吧。
事实上我们用条件判断和赋值语句就可以了。
unit_rooms = {3:{301:[1,80],302:[1,80],303:[2,90],304:[2,90]},
4:{401:[1,80],402:[1,80],403:[2,90],404:[2,90]},
5:{501:[1,80],502:[1,80],503:[2,90],504:[2,90]}
}
for sub_dict in unit_rooms.values():
for room,info in sub_dict.items():
if info[0] == 1:
info[0] = '南北'
elif info[0] == 2:
info[0] = '东西'
在这里,我也介绍另外一种方法,是对列表索引的活用,留意第7行和第9行的代码。
unit_rooms = {3:{301:[1,80],302:[1,80],303:[2,90],304:[2,90]},
4:{401:[1,80],402:[1,80],403:[2,90],404:[2,90]},
5:{501:[1,80],502:[1,80],503:[2,90],504:[2,90]}
}
for sub_dict in unit_rooms.values():
for room,info in sub_dict.items():
dire = ['', '南北', '东西']
#建立一个列表,第0个元素为空,第1个元素为'南北',第2个元素为'东西'
print(dire[info[0]])
最后一行:因为info[0]的值不是1就是2,所以dire[info[0]]不是dire[1]就是dire[2]。
如果是dire[1],就是取列表dire的第一个元素’南北’,dire[2]则取’东西’。所以代码就是:(直接运行即可)
unit_rooms = {3:{301:[1,80],302:[1,80],303:[2,90],304:[2,90]},
4:{401:[1,80],402:[1,80],403:[2,90],404:[2,90]},
5:{501:[1,80],502:[1,80],503:[2,90],504:[2,90]}
}
for sub_dict in unit_rooms.values():
for room,info in sub_dict.items():
dire = ['', '南北', '东西']
print('户室号:%d 朝向:%s 面积:%d' % (room,dire[info[0]],info[1]))
运行结果:
户室号:301 朝向:南北 面积:80
户室号:302 朝向:南北 面积:80
户室号:303 朝向:东西 面积:90
户室号:304 朝向:东西 面积:90
户室号:401 朝向:南北 面积:80
户室号:402 朝向:南北 面积:80
户室号:403 朝向:东西 面积:90
户室号:404 朝向:东西 面积:90
户室号:501 朝向:南北 面积:80
户室号:502 朝向:南北 面积:80
户室号:503 朝向:东西 面积:90
户室号:504 朝向:东西 面积:90
那么,截至目前,我们似乎把一切准备工作都做完了,我们来把前面的代码都整合在一起:
import csv
#调用csv模块
with open('assets.csv', 'a', newline='') as csvfile:
#调用open()函数打开csv文件,传入参数:文件名“assets.csv”、追加模式“a”、newline=''。
writer = csv.writer(csvfile, dialect='excel')
# 用csv.writer()函数创建一个writer对象。
header=['小区名称', '地址', '建筑年份', '楼栋', '单元', '户室', '朝向', '面积']
writer.writerow(header)
title=input('请输入小区名称:')
address = input('请输入小区地址:')
year = input('请输入小区建造年份:')
block = input('请输入楼栋号:')
unit=input('请输入单元号:')
start_floor = input('请输入起始楼层:')
end_floor = input('请输入终止楼层:')
# 开始输入模板数据
input('接下来请输入起始层每个房间的门牌号、南北朝向及面积,按任意键继续')
start_floor_rooms = {}
floor_last_number = []
# 收集起始层的房间信息
# 定义循环控制量
room_loop = True
while room_loop:
last_number = input('请输入起始楼层户室的尾号:(如01,02)')
floor_last_number.append(last_number)
#将尾号用append()添加列表里,如floor_last_number = ['01','02']
room_number = int(start_floor + last_number)
#户室号为room_number,由楼层start_floor和尾号last_number组成,如301
direction = int(input('请输入 %d 的朝向(南北朝向输入1,东西朝向输入2):' % room_number ))
area = int(input('请输入 %d 的面积,单位 ㎡ :' % room_number))
start_floor_rooms[room_number] = [direction,area]
# 户室号为键,朝向和面积组成的列表为值,添加到字典里,如start_floor_rooms = {301:[1,70]}
continued= input('是否需要输入下一个尾号?按 n 停止输入,按其他任意键继续:')
#加入打破循环的条件
if continued == 'n':
room_loop = False
else:
room_loop = True
unit_rooms = {}
#新建一个放单元所有户室数据的字典
unit_rooms[start_floor] = start_floor_rooms
#unit_rooms={3:{301:[1,80],302:[1,80],303:[2,90],304:[2,90]}}
for floor in range(int(start_floor) + 1, int(end_floor) + 1):
#遍历除初始楼层外的其他楼层
floor_rooms = {}
#每个楼层都建立一个字典
for i in range(len(start_floor_rooms)):
#遍历每层有多少个房间,这里是3,即执行for i in range 3 的循环
number = str(floor) + floor_last_number[i]
info = start_floor_rooms[int(start_floor + floor_last_number[i])]
# 依次取出字典start_floor_rooms键对应的值,即面积和朝向组成的列表
floor_rooms[int(number)] = info
#给字典floor_rooms添加键值对,floor_rooms = {401:[1,80]}
unit_rooms[floor] = floor_rooms
with open('assets.csv', 'a', newline='')as csvfile:
writer = csv.writer(csvfile, dialect='excel')
for sub_dict in unit_rooms.values():
for room,info in sub_dict.items():
dire = ['', '南北', '东西']
writer.writerow([title,address,year,block,unit,room,dire[info[0]],info[1]])
截至目前,我们只是完成了一个单元内所有户室的循环,考虑到一栋楼里可能会有多个单元,所以我们要在一开始再加入一层单元间的循环。我们的最后代码就变成这样:
import csv
#调用csv模块
with open('assets.csv', 'a', newline='') as csvfile:
#调用open()函数打开csv文件,传入参数:文件名“assets.csv”、追加模式“a”、newline=''。
writer = csv.writer(csvfile, dialect='excel')
# 用csv.writer()函数创建一个writer对象。
header=['小区名称', '地址', '建筑年份', '楼栋', '单元', '户室', '朝向', '面积']
writer.writerow(header)
title=input('请输入小区名称:')
address = input('请输入小区地址:')
year = input('请输入小区建造年份:')
block = input('请输入楼栋号:')
unit_loop = True
while unit_loop:
unit=input('请输入单元号:')
start_floor = input('请输入起始楼层:')
end_floor = input('请输入终止楼层:')
# 开始输入模板数据
input('接下来请输入起始层每个房间的门牌号、南北朝向及面积,按任意键继续')
start_floor_rooms = {}
floor_last_number = []
# 收集起始层的房间信息
# 定义循环控制量
room_loop = True
while room_loop:
last_number = input('请输入起始楼层户室的尾号:(如01,02)')
floor_last_number.append(last_number)
#将尾号用append()添加列表里,如floor_last_number = ['01','02']
room_number = int(start_floor + last_number)
#户室号为room_number,由楼层start_floor和尾号last_number组成,如301
direction = int(input('请输入 %d 的朝向(南北朝向输入1,东西朝向输入2):' % room_number ))
area = int(input('请输入 %d 的面积,单位 ㎡ :' % room_number))
start_floor_rooms[room_number] = [direction,area]
# 户室号为键,朝向和面积组成的列表为值,添加到字典里,如start_floor_rooms = {301:[1,70]}
continued= input('是否需要输入下一个尾号?按 n 停止输入,按其他任意键继续:')
#加入打破循环的条件
if continued == 'n':
room_loop = False
else:
room_loop = True
unit_rooms = {}
#新建一个放单元所有户室数据的字典
unit_rooms[start_floor] = start_floor_rooms
#unit_rooms={3:{301:[1,80],302:[1,80],303:[2,90],304:[2,90]}}
for floor in range(int(start_floor) + 1, int(end_floor) + 1):
#遍历除初始楼层外的其他楼层
floor_rooms = {}
#每个楼层都建立一个字典
for i in range(len(start_floor_rooms)):
#遍历每层有多少个房间,这里是3,即执行for i in range 3 的循环
number = str(floor) + floor_last_number[i]
info = start_floor_rooms[int(start_floor + floor_last_number[i])]
# 依次取出字典start_floor_rooms键对应的值,即面积和朝向组成的列表
floor_rooms[int(number)] = info
#给字典floor_rooms添加键值对,floor_rooms = {401:[1,80]}
unit_rooms[floor] = floor_rooms
with open('assets.csv', 'a', newline='')as csvfile:
#Mac用户要加多一个参数 encoding = 'GBK'
writer = csv.writer(csvfile, dialect='excel')
for sub_dict in unit_rooms.values():
for room,info in sub_dict.items():
dire = ['', '南北', '东西']
writer.writerow([title,address,year,block,unit,room,dire[info[0]],info[1]])
unit_continue = input('是否需要输入下一个单元?按 n 停止单元输入,按其他任意键继续:')
if unit_continue == 'n':
unit_loop = False
else:
unit_loop = True
print('恭喜你,资产录入工作完成!')
老规矩,又到了检验劳动成果的时候了!不过因为这里涉及到文件读写,所以你可以把csv的文件地址改成绝对路径,或者把代码复制到本地编辑器里,这样在py文件相同的文件夹里就能找到我们所新建的assets.csv文件了。
另外要注意的是,这个程序没有加上异常错误的处理,所以在运行时该输入数字时要输入数字,否则会报错。
你可能会觉得,能写出这个代码的时间都够我录入好多次了。但是你得这么想,一旦这个程序写好,它就能被使用很多次,甚至可以给很多人使用,这样能节省的时间远远大于你所付出的时间成本。
现在A君的同事们都在使用这个程序,大大提高了录入数据的效率,那又何乐不为呢?
那么,毕业项目我们就做到这里啦,这也意味着Python基础语法课也告一段落了。
结语
最后呢,是我写给你们的毕业信。
点开信的这一刻,恭喜你正式摆脱“真香”魔咒,把曾经立下的flag给拔了。现在你可以大声地对自己说:我真的入门Python了。
也许你是个在校学生,想点亮自己的技能树,为未来做好谋划;也许你是个勤恳的白领,遇到职业发展的瓶颈,想增值自己;也许你是个终身学习者,新知对你来说永远是最丰富的食粮。
而无论你来自天南地北,无论你怀着何种学习心态,此刻我都衷心地为你感到骄傲,我们都兑现了对彼此的承诺。
让我冒昧地来猜猜你这段时间以来可能发生在你身上的变化:
你可能练就了一双火眼金睛,对中英文符号变得异常地敏感和挑剔。
偶尔可能还会冒出浓浓“编程味”的哲思:人生就是一场大型循环,我们所有的努力都是在尝试break出这个循环。
你可能还能看懂以前get不了的梗,譬如说金句“人类的本质是复读机”用Python表示就是print(input())
你也可能对程序员多了一份复杂的情感(?),对bug多了一分宽容(或是厌恶)。
但更有可能的是:这段时间你发现你的头发越来越稀疏…
我欣喜于你身上的每一处细微的改变(好吧除了头秃),更不必说你能看懂并打出许多之前对你而言来说是异次元火星文的代码。
有个被问得烂俗的问题:你的梦想是什么?这个答案我想到了此刻我才有底气宣之于口:我希望人人都能懂点编程。
今天,程序员之间共享代码的平台有很多,但是编程教育的普及,离人人都会编程还有很长一段距离。
在我的理念里,打代码是件再稀松平常不过的事儿,程序员和“非程序员”之间不是二元对立的关系,技术也不应该成为不可逾越的壁垒。
我想做的就是尽可能地降低编程的准入门槛,让每个人都能享受到编程所带来的效率和乐趣。
在这里,我想用北野武老师的一番话与诸君共勉:
人类的智慧和想象力,因为撞上了墙壁遇到了障碍,所以会全面地发挥出来。智慧和想象力在突破了阻碍它们的那道墙时,会感受到自由的喜悦。愿这样的喜悦伴随你一生。
编程,恰恰就是凝聚了人类智慧和想象力的一门艺术啊。愿我们都能葆有持续的探索欲和毅力,翻过阻碍的高墙,不断挑战自己。
回顾这一段时间的学习历程,坚持到这里的你又有什么感受呢?
虽然我曾无数次为你放出烟火,但这一刻你值得最璀璨的烟火。
我宣布,你正式从基础语法课毕业了。从现在起,你可以前往更广阔的编程世界了!