11,092
社区成员
目录
接Day2的学习任务中,Day3我们将学会灵活的使用变量、 循环、 分支以及自定义函数
日常工作学习使用电脑的过程中,经常会在计算机中会散落大量的各种文件,整理这些文件会耽误很多时间,我们在这一次的任务中就来尝试使用Python语言来编写一个程序,帮助我们快速的分类整理这些文件,同时学习pythpn语言的更多语法知识。
程序最终形态:
Python finder.py <搜索目录> <文件类型> <目标目录>
通过Python程序指定搜索目录,在目录中搜索指定类型的文件,并且把文件移动到目标目录
这给过程中,我们需要用到许多知识点,其中一部分和磁盘文件目录相关的操作方法已经在上一个任务中训练过。
此前的任务都是在repl中运行的,而今天,我们需要亲自动手编写脚本。
把相关功能的代码集合以模块化形式组合在一起,可以被其他模块引用的源代码文件.py就是模块(module),允许在一个module中定义的功能在另一个module中重用。只要注意避免循环依赖,模块就是组织程序的一种简单而灵活的方式。
使用import语句导入模块,则模块中的代码将会被执行,无论使用pythpn命令运行模块还是使用repl导入模块。因此,直接将执行代码裸露再Python代码文件中不是一个值得推荐的做法。这会导致我们的部分代码无法测试,且裸露的代码只有在第一次import的时候被执行一次,这也使得有些程序无法受控的被调用。
正确的做法是将我们的模块用函数组织起来
关于Python函数定义的细节,请参考其他博客内容,这一章节里面我们专门来讲解代码和项目组织管理方式的问题。
print(name)可以查看当前代码运行时的模块名称。
如果是用Python命令运行app.py文件,则__name__ 的值是 __main__,而使用import导入到当前运行环境中的模块,__name__的值就是模块名称。
在正常编写项目时,我们希望直接使用pythpn命令运行脚本时,脚本能直接执行程序主要功能函数,但作为模块被导入到程序中,则不应该自动运行函数,函数应该是被受控的调用。
那么可以在模块中使用一种最佳实践
def main:
pass
if __name__ == '__main__':
main()
实际应用开发时,往往需要在运行Python程序的同时传递执行参数,有些程序员可能会为零方便,直接import sys,然后再main函数中通过sys.argv1取命令执行参数,但这又会导致main函数无法被测试脚本测试,因为脚本作为repl导入模块时,程序无法取得sys.argv1。
正确的做法是main函数指定需要接收的参数,然后由main函数调用时注入参数。
def main(fileName):
pass
if __name__ == '__main__':
main(sys.argv[1])
以上时单文件模块作为可执行程序发布时的最佳实践,这种使用方法在Linux自动化任务脚本编写时十分常用,在编写MapReduce数据分析程序时,也是十分有效的。
顺便提一下,在CSDN能力测评的Python工程师基础能力认证(C4)以及 全栈工程师底层能力认证(C5)的考核中,也需要类似的技巧来编写程序。
此前我们已经在repl使用过普通变量,普通变量可以保存一个数据,比如a=3又或者str=‘hello’。不过这次的任务我们需要从文件夹中搜索的文件可能会有很多个,因此我们需要有一种特殊的容器类型,它能用一个变量就保存我们搜索到的所有文件路径,这种能保存多个对象的数据类型称为容器类型。
Python 中的list,是对象序列。与字符串不同,list的是可变的,因为其中的元素可以被替换或删除,并且可以插入或附加新元素。list 是 Python 数据结构的主力(其他容器类型在本次任务中没有使用,因此不做详细介绍)。
用中括号圈起来的数据就是 list,多个数据由逗号分隔。
>>> [1, 9, 8]
[1, 9, 8]
这是放入3个数字的list
>>> a = ["apple", "orange", "pear"]
这是放了三个字符串的list,我们文件对应的路径本质上也是字符串
我们可以使用带有从零开始的索引的方括号来检索元素:
>>> a[1]
"orange"
我们可以通过分配给特定元素来替换元素:
>>> a[1] = 7
>>> a
['apple', 7, 'pear']
创建一个空列表通常很有用,我们可以使用空方括号来实现:
>>> b = []
我们可以通过append()方法在list末尾添加一个元素 :
>>> b.append(1.618)
>>> b
[1.618]
>>> b.append(1.414)
[1.618, 1.414]
还有许多其他有用的方法来操作lists,现在,我们只需要能够执行基本list操作就能完成此次任务。
尽管我们可以在脚本文件中像repl一样,一行一行编写代码按顺序执行,不过项目复杂以后这样维护和管理都变得困难。
所有编程语言都都提供了函数用来解决代码抽象分层的问题,Python的函数使用起来也很简单
#直接编写的裸代码会直接运行
>>> 1+1
2
>>> def f():
... """函数名f,函数的参数放到括号中
... 函数名和参数后紧接一个冒号,然后换行进入函数体
... 函数体中的代码由四个空格缩进对齐,
... 三个引号引起来的字符串是函数说明文档"""
... a=3
... b=4
... print(a+b)
...
>>> #默认def声明的函数不会立刻执行,但通过函数名加括号调用函数,函数中的代码块会立刻执行
>>> f()
7
本次任务中除了刚才给大家铺垫的知识,还有一个难点,可以说是新手噩梦,不过我们并不急于让大家在一次任务就掌握它,这个噩梦就是递归。
百度百科对递归的定义是这样的:
看起来很懵逼是不是,简单的说就是程序自己调用自己。
我们的任务中需要在文件夹中搜索目标文件,但是文件夹中可能还会包含其他文件夹,因此遇到文件夹中还有其他文件夹的时候,就需要搜索函数再调用自己。
本次任务我们直接给出了部分代码,只要知道有递归这种操作方法即可,具体递归代码不需要大家编写,语法熟练以后,再专门训练算法才事半功倍。
在自己的工作目录中创建一个文件夹,并进入文件夹
mkdir finder && cd finder
touch finder.py
如果有安装vscode,那么可以直接使用以下命令打开项目
code .
现在我们可以编辑我们的脚本文件
from os import sys, path, listdir
def search(root, target):
'定义搜索文件函数,接收两个参数,第一个参数是搜索的根目录,
第二个参数是搜索的目标文件类型'''
#定义一个数组,用于存储找到的文件的路径
fileList=[]
#取出搜索根目录下的文件和文件夹放入items数组
items = listdir(root)
print(items)
#依次检查,如果是目录就继续向下搜索,如果是文件则判断文件类型是否匹配
for item in items:
filepath = path.join(root, item)
if path.isdir(filepath):
search(filepath, target)
elif path.isfile(filepath):
if filepath.split('.')[-1] == target:
fileList.append(filepath)
print('[+]', filepath)
return fileList
def moveFileToDest(files,dest):
pass
def main(argv):
path = argv[1]
target = argv[2]
dest = argv[3]
files=search(path,target)
moveFileToDest(files,dest)
print(files)
if __name__ == '__main__':
main(sys.argv)
这个程序中moveFileToDest函数就留给你来实现了,补充完整后,我们可以使用
Python finder.py "C:\Documents and Settings\你的用户名\Local Settings\Temp" docx "C:\USER\用户名\桌面\docx"
这样一条命令就将自己计算机中默认下载目录中所有的docx文档保存到你的桌面的docx文件夹中。(前提是你自己的桌面要有一个docx文件夹,如果没有可以创建一个)
Day3练习作业:
详细阅读学习任务,并对其中的代码、操作亲自执行实验。
作业提交
你可以这样做(任选其一):
提交作业步骤:
作业提交截止时间:
2021/9/19 19:00