<jupyter_start><jupyter_text>基本库的使用基本库的运用可以从这些方面着手：- 数据结构操作- 日期时间表达- 文件读写- 图像操作- 进程和线程- 系统模块- 电子邮件- 调用其他语言程序以下各小节，没有特别说明的话，都是主要引用了[廖雪峰python教程](https://www.liaoxuefeng.com/wiki/1016959663602400) 数据结构基本操作<jupyter_code># all()函数
print(all(['a', 'b', 'c', 'd']))  # 列表list，元素都不为空或0
print(all(['a', 'b', '', 'd']))
print(all([0, 1, 2, 3]))
print(all([]))
print(all(()))

# eval()函数
x = 7
# eval(expression[, globals[, locals]])用来执行一个字符串表达式，并返回表达式的值。
print(eval('3*x'))
print(eval('2+2'))
print(eval('pow(2,2)'))<jupyter_output>True
False
False
True
True
21
4
4<jupyter_text>日期时间datetime是Python处理日期和时间的标准库。获取当前日期和时间：<jupyter_code>from datetime import datetime
now = datetime.now() # 获取当前datetime
print(now)<jupyter_output>2021-07-29 17:52:30.434800<jupyter_text>注意到datetime是模块，datetime模块还包含一个datetime类，通过from datetime import datetime导入的才是datetime这个类。如果仅导入import datetime，则必须引用全名datetime.datetime。datetime.now()返回当前日期和时间，其类型是datetime。<jupyter_code>from datetime import datetime, timedelta
dt = datetime(2015, 4, 19, 12, 20) # 用指定日期时间创建datetime
print(dt)
# str转换为datetime
cday = datetime.strptime('2015-6-1 18:19:59', '%Y-%m-%d %H:%M:%S')
print(cday)
# 年月日
text = '2012-09-20'
y = datetime.strptime(text, '%Y-%m-%d')
print(y)
# datetime转换为str
print(now.strftime('%a, %b %d %H:%M'))
# datetime加减
print(now + timedelta(hours=10))
now + timedelta(days=2, hours=12)

import calendar
print (calendar.isleap(1996))<jupyter_output>2015-04-19 12:20:00
2015-06-01 18:19:59
2012-09-20 00:00:00
Thu, Jul 29 17:52
2021-07-30 03:52:30.434800
True<jupyter_text>有时候会用到julian date，julian date 儒略日时间 是指一年中的第几天，日期转julian date的方式可如下所示。<jupyter_code>import datetime
fmt = '%Y-%m-%d'
s = '2012-11-07'
dt = datetime.datetime.strptime(s, fmt)
dt
tt = dt.timetuple()
tt.tm_yday<jupyter_output><empty_output><jupyter_text>IO编程IO就是输入输出，IO中有一个很重要的概念，就是流Stream，数据从网络、硬盘流入内存是Input stream，反过来就是output stream。控制IO的方式有同步和异步两种，这是因为内存存取数据的速度是远高于外设的，因此就存在速度不匹配的问题，所以要么就忍者，快的等慢的，即同步的；要么就不等，快的告诉慢的你先弄着，我先去干别的，搞完了告诉我，这就是异步。异步优点是性能好，缺点是麻烦。操作IO的能力都是操作系统提供的，每一种编程语言都会把操作系统提供的低级C接口封装起来方便使用，Python也不例外。接下来就从一些常用操作开始说起。 读写txt文件在磁盘上读写文件的功能都是由操作系统提供的，现代操作系统不允许普通的程序直接操作磁盘，所以，读写文件就是**请求操作系统打开一个文件对象**（通常称为文件描述符），然后，通过**操作系统提供的接口**从这个文件对象中读取数据（读文件），或者把数据写入这个文件对象（写文件）。要以读文件的模式打开一个文件对象，使用Python内置的**open()函数**，传入文件名和标示符：<jupyter_code># open函数请求操作系统打开文件
f=open('test.txt','r')
# 打开后读取文件内容到内存中，是一个str对象
f.read()<jupyter_output><empty_output><jupyter_text>最后，要记得关闭文件，因为其会占用操作系统的资源，操作系统同一时间能打开的文件数量是有限的。<jupyter_code>f.close()<jupyter_output><empty_output><jupyter_text>前面说过with的用法，因为有时候可能会有错误，所以读写文件这类操作通常会有try...finally来实现，但是这样写太麻烦，所以使用with语句。<jupyter_code>with open('test.txt', 'r') as f:
    print(f.read())<jupyter_output>Hello, world!
Hello, Owen!<jupyter_text>如果文件很小，read()一次性读取最方便；如果不能确定文件大小，反复调用read(size)比较保险；如果是配置文件，调用readlines()最方便：<jupyter_code>with open('test.txt', 'r') as f:
    for line in f.readlines():
        print(line.strip()) # 把末尾的'\n'删掉<jupyter_output>Hello, world!
Hello, Owen!<jupyter_text>另外还有readline方法，该方法比较适合读取大文件，for line in f将文件对象f视为一个迭代器，自动的采用缓冲IO和内存管理，所以不必担心大文件。让系统来处理，其实是最简单的方式，交给解释器，就万事大吉了。——[python读GB级大文件](https://github.com/Shuang0420/Shuang0420.github.io/wiki/python%E8%AF%BBGB%E7%BA%A7%E5%A4%A7%E6%96%87%E4%BB%B6)<jupyter_code>with open('test.txt', 'r') as f:
    for line in f:
        print(line.strip()) # 把末尾的'\n'删掉<jupyter_output>Hello, world!
Hello, Owen!<jupyter_text>这三种方法的区别，参考：[Python中的read(),readline(),readlines()区别与用法](https://www.jianshu.com/p/a672f39287c4)- read([size])方法从文件当前位置起读取size个字节，若无参数size，则表示读取至文件结束为止，它范围为字符串对象；- 从字面意思可以看出，该方法每次读出一行内容，所以，读取时占用内存小，比较适合大文件，该方法返回一个字符串对象；- readlines()方法读取整个文件所有行，保存在一个列表(list)变量中，每行作为一个元素，但读取大文件会比较占内存。写文件和读文件是一样的，唯一区别是调用open()函数时，传入标识符'w'或者'wb'表示写文本文件或写二进制文件：<jupyter_code>with open('test.txt', 'w') as f:
    f.write('Hello, world!')<jupyter_output><empty_output><jupyter_text>以'w'模式写入文件时，如果文件已存在，会直接覆盖（相当于删掉后新写入一个文件）。如果我们希望追加到文件末尾怎么办？可以传入'a'以追加（append）模式写入。<jupyter_code>with open('test.txt', 'a') as f:
    # 字符串开头加\n，可以另起一行开始添加    
    f.write('\nHello, Owen!')<jupyter_output><empty_output><jupyter_text>读写json文件json 模块提供了一种很简单的方式来编码和解码JSON数据。 其中两个主要的函数是 json.dumps() 和 json.loads()，要比其他序列化函数库如pickle的接口少得多。下面演示如何将一个Python数据结构转换为JSON<jupyter_code>import json
# 写入json
data = {
    'name' : 'ACME',
    'shares' : 100,
    'price' : 542.23
}

json_str = json.dumps(data)

# 读取
data = json.loads(json_str)<jupyter_output><empty_output><jupyter_text>如果你要处理的是文件而不是字符串，你可以使用 json.dump() 和 json.load() 来编码和解码JSON数据。例如：<jupyter_code># Writing JSON data
with open('data.json', 'w') as f:
    json.dump(data, f)

# Reading data back
with open('data.json', 'r') as f:
    data = json.load(f)<jupyter_output><empty_output><jupyter_text>JSON编码支持的基本数据类型为 None ， bool ， int ， float 和 str ， 以及包含这些类型数据的lists，tuples和dictionaries。对于dictionaries，keys需要是字符串类型(字典中任何非字符串类型的key在编码时会先转换为字符串)。为了遵循JSON规范，你应该只编码Python的lists和dictionaries。 而且，在web应用程序中，顶层对象被编码为一个字典是一个标准做法。在编码JSON的时候，还有一些选项很有用。 如果你想获得漂亮的格式化字符串后输出，可以使用 json.dumps() 的indent参数。 它会使得输出和pprint()函数效果类似。<jupyter_code>print(json.dumps(data))
print(json.dumps(data, indent=4))<jupyter_output>{"name": "ACME", "shares": 100, "price": 542.23}
{
    "name": "ACME",
    "shares": 100,
    "price": 542.23
}<jupyter_text>序列化把变量**从内存中变成可存储或传输的过程**称之为序列化，在Python中叫**pickling**，在其他语言中也被称之为serialization，marshalling，flattening等等，都是一个意思。序列化之后，就可以把**序列化**后的内容写入磁盘，或者通过网络传输到别的机器上。反过来，把变量内容从序列化的对象重新读到内存里称之为**反序列化**，即unpickling。Python提供了pickle模块来实现序列化。比如尝试把一个对象序列化并写入文件：<jupyter_code>import pickle
d = dict(name='Bob', age=20, score=88)
pickle.dumps(d)<jupyter_output><empty_output><jupyter_text>pickle.dumps()方法把任意对象**序列化成一个bytes**，然后，就**可以把这个bytes写入文件**。或者用另一个方法pickle.dump()直接把对象序列化后**写入一个file-like Object**：<jupyter_code>f = open('dump.txt', 'wb')
pickle.dump(d, f)
f.close()<jupyter_output><empty_output><jupyter_text>看看写入的dump.txt文件，一堆乱七八糟的内容，这些都是Python保存的对象内部信息。当我们要把对象从磁盘读到内存时，可以**先把内容读到一个bytes**，然后**用pickle.loads()方法反序列化出对象**，也可以**直接用pickle.load()方法从一个file-like Object中直接反序列化出对象**。我们打开另一个Python命令行来反序列化刚才保存的对象：<jupyter_code>f = open('dump.txt', 'rb')
d = pickle.load(f)
f.close()
d<jupyter_output><empty_output><jupyter_text>注意，这个变量和原来的变量是完全不相干的对象，它们只是内容相同而已。Pickle的问题和所有其他编程语言特有的序列化问题一样，就是它只能用于Python，并且可能不同版本的Python彼此都不兼容，因此，只能用Pickle保存那些不重要的数据，不能成功地反序列化也没关系。如果我们要在不同的编程语言之间传递对象，就必须把对象序列化为标准格式，比如XML，但更好的方法是序列化为**JSON**，因为JSON表示出来就是一个字符串，可以被所有语言读取，也可以方便地存储到磁盘或者通过网络传输。JSON不仅是标准格式，并且比XML更快，而且可以直接在Web页面中读取，非常方便。不过JSON格式的类型转换是有限的，所以一般是在传输参数时使用，具体使用前面已经提及。对于一般自定义的各种类，还是使用pickle模块。对于Python数据被不同机器上的解析器所共享的应用程序而言， 数据的保存可能会有问题，因为所有的机器都必须访问同一个源代码。另外，pickle 对于**大型的数据结构**比如使用 array 或 numpy 模块创建的二进制数组效率**并不是一个高效的编码方式**。 如果你需要移动大量的数组数据，你最好是先在一个文件中将其保存为数组数据块或使用更高级的标准编码方式如**HDF5** (需要第三方库的支持)。还有，由于 pickle 是Python特有的并且附着在源码上，所有如果需要**长期存储数据的时候不应该选用它**。  图像基操PIL：Python Imaging Library，已经是Python平台事实上的图像处理标准库了。PIL功能非常强大，但API却非常简单易用。由于PIL仅支持到Python 2.7，加上年久失修，于是一群志愿者在PIL的基础上创建了兼容的版本，名字叫**Pillow**，支持最新Python 3.x，又加入了许多新特性，因此，我们可以直接使用Pillow。安装 pillow：```Shellconda install -c conda-forge pillow```下面看一个图像缩放操作。<jupyter_code>from PIL import Image

# 打开一个jpg图像文件，注意是当前路径:
im = Image.open('pictures/test.jpg')
# 获得图像尺寸:
w, h = im.size
print('Original image size: %sx%s' % (w, h))
# 缩放到50%:
im.thumbnail((w//2, h//2))
print('Resize image to: %sx%s' % (w//2, h//2))
# 把缩放后的图像用jpeg格式保存:
im.save('pictures/thumbnail.jpg', 'jpeg')<jupyter_output>Original image size: 500x300
Resize image to: 250x150<jupyter_text>其他功能如切片、旋转、滤镜、输出文字、调色板等一应俱全。比如，模糊效果也只需几行代码：<jupyter_code>from PIL import Image, ImageFilter

# 打开一个jpg图像文件，注意是当前路径:
im = Image.open('pictures/test.jpg')
# 应用模糊滤镜:
im2 = im.filter(ImageFilter.BLUR)
im2.save('pictures/blur.jpg', 'jpeg')<jupyter_output><empty_output><jupyter_text>补充一个拼接操作，主要参考了：[python实现两张图片拼接为一张图片并保存](https://www.jb51.net/article/165457.htm)首先看横向拼接。<jupyter_code>import os
from os import listdir
from PIL import Image
# 先删除之前拼接的结果
# os.remove("pictures/res1.jpg")
# 获取当前文件夹中所有JPG图像
im_list = [Image.open(fn) for fn in listdir() if fn.endswith('.jpg')]

im = Image.open('pictures/test.jpg')
# 获得图像尺寸:
w, h = im.size

# 图片转化为相同的尺寸
ims = []
for i in im_list:
    new_img= i.resize((w, h), Image.BILINEAR)
    ims.append(new_img)
    
# 单幅图像尺寸
width, height = ims[0].size
  
# 创建空白长图
result = Image.new(ims[0].mode, (width * len(ims), height))

# 拼接图片
for i, im in enumerate(ims):
    # left是左上角的横坐标，依次递增
    # 将image复制到target的指定位置中,The position to paste is specified by a tuple (x coordinate in upper left, y coordinate in upper left) in the second parameter box
    result.paste(im, box=(i * width, 0))
# 保存图片
result.save('pictures/res1.jpg')<jupyter_output><empty_output><jupyter_text>如果想要图片高度一样，宽度不同，可以尝试：<jupyter_code>import os
from os import listdir
from PIL import Image
# 先删除之前拼接的结果
# os.remove("pictures/res2.jpg")
# 获取当前文件夹中所有JPG图像
im_list = [Image.open(fn) for fn in ["pictures/test.jpg", "pictures/thumbnail.jpg"]]

im = Image.open("pictures/test.jpg")
# 获得图像尺寸:
w, h = im.size

# 图片都转换为第一个图片相同的高度，宽度根据高度变化的比例等比例调整
ims = []
for i in range(1, len(im_list)):
    new_img = im_list[i].resize((w, h), Image.BILINEAR)
    ims.append(new_img)
    
# 单幅图像尺寸
width, height = ims[0].size
  
# 创建空白长图
result = Image.new(ims[0].mode, (width * len(ims), height))

# 拼接图片
for i, im in enumerate(ims):
    # left是左上角的横坐标，依次递增
    # 将image复制到target的指定位置中,The position to paste is specified by a tuple (x coordinate in upper left, y coordinate in upper left) in the second parameter box
    result.paste(im, box=(i * width, 0))
# 保存图片
result.save('pictures/res2.jpg')<jupyter_output><empty_output><jupyter_text>进程和线程现代操作系统基本都是支持“多任务”的操作系统。“多任务”，简单地说，就是操作系统可以同时运行多个任务。因为CPU的速度很快，为了不浪费资源，所以单核的时候就有让操作系统轮流执行多任务的犀利操作了。而有了多核CPU之后，就有了真正的并行多任务。但是，由于任务数量远远多于CPU的核心数量，所以，操作系统也会自动把很多任务轮流调度到每个核心上执行。对于操作系统来说，一个任务就是一个进程（Process），比如打开一个浏览器就是启动一个浏览器进程。有些进程还不止同时干一件事，比如Word，它可以同时进行打字、拼写检查、打印等事情。在一个进程内部，要同时干多件事，就需要同时运行多个“子任务”，我们把进程内的这些“子任务”称为线程（Thread）。关于进程和线程的区别可以参考这篇[post](https://www.zhihu.com/question/25532384)。引用一句总结：线程共享了进程的上下文环境，是颗粒更小的CPU时间段的描述。回到python，之前的程序基本都是单任务的进程，也就是只有一个线程，现在如果要同时执行多个任务怎么办？多任务的实现有3种方式：- 多进程模式；- 多线程模式；- 多进程+多线程模式启动多个进程，每个进程再启动多个线程，这种模型更复杂，实际很少采用。同时执行多个任务通常各个任务之间并不是没有关联的，而是需要相互通信和协调，有时，任务1必须暂停等待任务2完成后才能继续执行，有时，任务3和任务4又不能同时执行，所以，多进程和多线程的程序的**复杂度要远远高于**我们前面写的单进程单线程的程序。不是迫不得已，也不想编写多任务。但是，有很多时候，没有多任务还真不行。想想在电脑上看电影，就必须由一个线程播放视频，另一个线程播放音频，否则，单线程实现的话就只能先把视频播放完再播放音频，或者先把音频播放完再播放视频，这显然是不行的。 多进程Unix/Linux操作系统提供了一个fork()系统调用，它非常特殊。普通的函数调用，调用一次，返回一次，但是**fork()调用一次，返回两次**，因为操作系统自动把当前进程（称为父进程）复制了一份（称为子进程），然后，分别在父进程和子进程内返回。子进程永远返回0，而父进程返回子进程的ID。这样做的理由是，一个父进程可以fork出很多子进程，所以，父进程要记下每个子进程的ID，而子进程只需要调用getppid()就可以拿到父进程的ID。Python的os模块封装了常见的系统调用，其中就包括fork，可以在Python程序中轻松创建子进程，不过注意在windows下是会报错的：<jupyter_code>import os

print('Process (%s) start...' % os.getpid())
# Only works on Unix/Linux/Mac:
pid = os.fork()
if pid == 0:
    print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid()))
else:
    print('I (%s) just created a child process (%s).' % (os.getpid(), pid))<jupyter_output>Process (11388) start...<jupyter_text>从上面可以看到if和else后的语句都被执行了。有了fork调用，**一个进程在接到新任务时就可以复制出一个子进程来处理新任务**，常见的Apache服务器就是由父进程监听端口，每当有新的http请求时，就fork出子进程来处理新的http请求。如果打算编写多进程的服务程序，Unix/Linux无疑是正确的选择。那么Windows没有fork调用，怎么办呢？python提供的**multiprocessing模块**是跨平台版本的多进程模块。multiprocessing模块提供了一个**Process类**来代表一个**进程对象**，下面的例子演示了启动一个子进程并等待其结束：<jupyter_code>from multiprocessing import Process
import os

# 子进程要执行的代码
def run_proc(name):
    print('Run child process %s (%s)...' % (name, os.getpid()))

if __name__=='__main__':
    print('Parent process %s.' % os.getpid())
    p = Process(target=run_proc, args=('test',))
    print('Child process will start.')
    p.start()
    p.join()
    print('Child process end.')<jupyter_output>Parent process 11388.
Child process will start.
Child process end.<jupyter_text>创建子进程时，只需要**传入一个执行函数和函数的参数**，**创建一个Process实例**，用**start()方法启动**。join()方法可以等待子进程结束后再继续往下运行，通常用于进程间的同步。 Process之间肯定是需要通信的，操作系统提供了很多机制来实现进程间的通信。Python的multiprocessing模块包装了底层的机制，提供**Queue、Pipes等多种方式来交换数据**。<jupyter_code>from multiprocessing import Process, Queue
import os, time, random

# 写数据进程执行的代码:
def write(q):
    print('Process to write: %s' % os.getpid())
    for value in ['A', 'B', 'C']:
        print('Put %s to queue...' % value)
        q.put(value)
        time.sleep(random.random())

# 读数据进程执行的代码:
def read(q):
    print('Process to read: %s' % os.getpid())
    while True:
        value = q.get(True)
        print('Get %s from queue.' % value)

if __name__=='__main__':
    # 父进程创建Queue，并传给各个子进程：
    q = Queue()
    pw = Process(target=write, args=(q,))
    pr = Process(target=read, args=(q,))
    # 启动子进程pw，写入:
    pw.start()
    # 启动子进程pr，读取:
    pr.start()
    # 等待pw结束:
    pw.join()
    # pr进程里是死循环，无法等待其结束，只能强行终止:
    pr.terminate()<jupyter_output><empty_output><jupyter_text>访问操作系统功能这部分参考了[简书博客](https://www.jianshu.com/p/5ce082723fe6)。OS模块是Python标准库中的一个用于**访问操作系统功能**的模块，OS模块提供了一种可移植的方法使用操作系统的功能。使用OS模块中提供的接口，可以实现跨平台访问。但是在OS模块中的接口并不是所有平台都通用，有些接口的实现是依靠特定平台下的接口的。在OS模块中提供了一系列访问操作系统功能的接口，便于编写跨平台的应用。<jupyter_code>import os
# 系统名称，windows下为‘nt’，linux下为‘posix’
os.name<jupyter_output><empty_output><jupyter_text>在使用OS模块的时候，如果使用过程中出现了异常，OS模块会抛出OSError异常，表明：无效的路径名或文件名，或者给出的路径名或文件名无法访问，或者当前使用的系统不支持。这里给出一些常用的os命令：<jupyter_code># 系统用户根目录
# print (os.environ['HOME'])
print( os.path.expandvars('$HOME'))
print (os.path.expanduser('~'))
# 得到当前工作的目录
print(os.getcwd())
# 列出指定目录（默认是当前目录）下所有的文件和目录名
os.listdir()
# 判断指定对象是否为文件
print(os.path.isfile('test.txt'))
# 判断指定对象是否为目录
print(os.path.isdir('test.txt'))
# 检验指定的对象是否存在，文件夹和文件略有不同
print(os.path.isdir("test"))
print(os.path.exists('test.txt'))
# 返回路径的目录和文件名
print(os.path.split('test.txt'))
# 连接目录和文件名
os.path.join('1-basin-python', 'test.txt')<jupyter_output>$HOME
C:\Users\hust2
C:\Projects\git\hydrus\1-learn-python
True
False
True
True
('', 'test.txt')<jupyter_text>在实际应用中，经常会先判断一个文件夹是否存在，如果不存在，就会调用os.mkdir来生成文件夹，不过有时候，可能不存在的文件夹包括多层，这时候就需要调用os.makedirs 才能创建了。<jupyter_code>import os
dir_name="test/test/test/test/test"
if not os.path.isdir(dir_name):
    os.makedirs(dir_name)
dir_name="test/test/test/test/test/test"
if not os.path.isdir(dir_name):
    os.makedirs(dir_name)
# 执行shell命令
os.system("echo 'hello world!'")<jupyter_output><empty_output><jupyter_text>补充一些os.path.join()的内容：- 该函数会从第一个以”/”开头的参数开始拼接，之前的参数全部丢弃；- 在上一种情况确保情况下，若出现”./”开头的参数，会从”./”开头的参数的上一个参数开始拼接。<jupyter_code>print("1:",os.path.join('aaaa','/bbbb','ccccc.txt'))

print("2:",os.path.join('/aaaa','/bbbb','/ccccc.txt'))

print("3:",os.path.join('aaaa','./bbb','ccccc.txt'))
# 获取上上上级目录
os.path.abspath(os.path.join(os.getcwd(), "../../.."))<jupyter_output>1: /bbbb\ccccc.txt
2: /ccccc.txt
3: aaaa\./bbb\ccccc.txt<jupyter_text>如何列出指定目录下的文件？根据网络查询结果，比如[python获取指定目录下所有文件名os.walk和os.listdir](https://www.cnblogs.com/cloud-ken/p/10017093.html)，主要有两种方式，os.walk和os.listdir。- os.walk会遍历指定目录下的所有子文件夹和子文件夹中的所有文件；- os.listdir返回指定路径下所有的文件和文件夹列表,但是子目录下文件不遍历。下面先给出os.walk的示例（列出所有文件和文件夹）：<jupyter_code>import os
def file_name_walk(file_dir):
    for root, dirs, files in os.walk(file_dir):
        print("root", root)  # 当前目录路径
        print("dirs", dirs)  # 当前路径下所有子目录
        print("files", files)  # 当前路径下所有非目录子文件
        print("\n") 

file_name_walk("./")<jupyter_output>root ./
dirs ['.ipynb_checkpoints', 'pictures', 'test', '__pycache__']
files ['1.1-basic-python.ipynb', '1.2-basic-python.ipynb', '1.3-basic-python.ipynb', '1489720930-5882-BO4qO.png', '1489720930-6827-Vtk4m.png', '1489720931-7116-4AQC6.png', '2-advanced-python.ipynb', 'app.py', 'argv_argparse.py', 'comparison.png', 'config.ini', 'data.json', 'dump.txt', 'globalLog.py', 'hello.py', 'mydict.py', 'mydict2.py', 'mydict_test.py', 'mymodule.py', 'my_calendar.py', 'res1.jpg', 'server.py', 'simple.py', 'something.py', 'something_all.py', 'test.txt', 'test1.txt']


root ./.ipynb_checkpoints
dirs []
files ['1.1-basic-python-checkpoint.ipynb', '1.2-basic-python-checkpoint.ipynb']


root ./pictures
dirs []
files ['6056x8a7rwk1hv9h4oot.png', 'blur.jpg', 'res1.jpg', 'res2.jpg', 'test.jpg', 'thumbnail.jpg']


root ./test
dirs ['test']
files ['test.zip', 'test1.txt']


root ./test\test
dirs ['1', 'test']
files ['1 - Copy.txt', '1.txt', '1.zip']


root ./test\test\1
dirs []
files ['1 - Copy (2).txt', [...]<jupyter_text>接下来看看os.listdir()等方法，主要参考：[Python文件操作，看这篇就足够](https://zhuanlan.zhihu.com/p/56909212)。在现代Python版本中，可以使用 os.listdir() ，os.scandir() 和 pathlib.Path() 打印出目录中文件的名称。为了过滤目录并仅列出 os.listdir() 生成的目录列表的文件，要使用 os.path 。在这里调用 os.listdir() 返回指定路径中所有内容的列表，接着使用 os.path.isfile() 过滤列表让其只显示文件类型而非目录类型。<jupyter_code>import os

basepath = './'
for entry in os.listdir(basepath):
    # 使用os.path.isfile判断该路径是否是文件类型
    if os.path.isfile(os.path.join(basepath, entry)):
        print(entry)<jupyter_output>1.1-basic-python.ipynb
1.2-basic-python.ipynb
1.3-basic-python.ipynb
1489720930-5882-BO4qO.png
1489720930-6827-Vtk4m.png
1489720931-7116-4AQC6.png
2-advanced-python.ipynb
app.py
argv_argparse.py
comparison.png
config.ini
data.json
dump.txt
globalLog.py
hello.py
mydict.py
mydict2.py
mydict_test.py
mymodule.py
my_calendar.py
res1.jpg
server.py
simple.py
something.py
something_all.py
test.txt
test1.txt<jupyter_text>一个更简单的方式来列出一个目录中所有的文件是使用 os.scandir() 或 pathlib.Path() :<jupyter_code>import os

basepath = './'
with os.scandir(basepath) as entries:
    for entry in entries:
        if entry.is_file():
            print(entry.name)<jupyter_output>1.1-basic-python.ipynb
1.2-basic-python.ipynb
1.3-basic-python.ipynb
1489720930-5882-BO4qO.png
1489720930-6827-Vtk4m.png
1489720931-7116-4AQC6.png
2-advanced-python.ipynb
app.py
argv_argparse.py
comparison.png
config.ini
data.json
dump.txt
globalLog.py
hello.py
mydict.py
mydict2.py
mydict_test.py
mymodule.py
my_calendar.py
res1.jpg
server.py
simple.py
something.py
something_all.py
test.txt
test1.txt<jupyter_text>接着，展示如何使用 pathlib.Path() 列出一个目录中的文件：<jupyter_code>from pathlib import Path

basepath = Path('./')
for entry in basepath.iterdir():
    if entry.is_file():
        print(entry.name)<jupyter_output>1.1-basic-python.ipynb
1.2-basic-python.ipynb
1.3-basic-python.ipynb
1489720930-5882-BO4qO.png
1489720930-6827-Vtk4m.png
1489720931-7116-4AQC6.png
2-advanced-python.ipynb
app.py
argv_argparse.py
comparison.png
config.ini
data.json
dump.txt
globalLog.py
hello.py
mydict.py
mydict2.py
mydict_test.py
mymodule.py
my_calendar.py
res1.jpg
server.py
simple.py
something.py
something_all.py
test.txt
test1.txt<jupyter_text>如果将for循环和if语句组合成单个生成器表达式，则上述的代码可以更加简洁。<jupyter_code>from pathlib import Path

basepath = Path('./')
files_in_basepath = (entry for entry in basepath.iterdir() if entry.is_file())
for item in files_in_basepath:
    print(item.name)<jupyter_output>1.1-basic-python.ipynb
1.2-basic-python.ipynb
1.3-basic-python.ipynb
1489720930-5882-BO4qO.png
1489720930-6827-Vtk4m.png
1489720931-7116-4AQC6.png
2-advanced-python.ipynb
app.py
argv_argparse.py
comparison.png
config.ini
data.json
dump.txt
globalLog.py
hello.py
mydict.py
mydict2.py
mydict_test.py
mymodule.py
my_calendar.py
res1.jpg
server.py
simple.py
something.py
something_all.py
test.txt
test1.txt<jupyter_text>获取目录中的文件列表后，可能希望搜索和特定的模式匹配的文件。可以使用的方法和函数：- endswith() 和 startswith() 字符串方法- fnmatch.fnmatch()- glob.glob()- pathlib.Path.glob()这里就用第一种方法做例子。当在匹配文件名时，其中的两个方法 .startswith() 和 .endswith() 非常有用。要做到这点，首先要获取一个目录列表，然后遍历。<jupyter_code>import os

for f_name in os.listdir('./'):
    if f_name.endswith('.txt'):
        print(f_name)
import os

for f_name in os.listdir('./'):
    if f_name.startswith('test'):
        print(f_name)<jupyter_output>test
test.txt
test1.txt<jupyter_text>从上述结果可以看出，上述代码找到当前文件夹中的所有文件，遍历并使用 .endswith() 来打印了所有扩展名为 .txt 的文件名。字符串方法匹配的能力是有限的。fnmatch 有对于模式匹配有更先进的函数和方法。接下来的例子考虑使用 fnmatch.fnmatch() ，这是一个支持使用 * 和 ? 等通配符的函数。例如，使用 fnmatch 查找目录中所有 .txt 文件：<jupyter_code>import os
import fnmatch

for f_name in os.listdir('./'):
    if fnmatch.fnmatch(f_name, '*.txt'):
        print(f_name)<jupyter_output>dump.txt
test.txt
test1.txt<jupyter_text>有时候需要更先进的模式匹配，假设想要查找符合特定条件的 .ipynb 文件。例如下面两个例子：<jupyter_code>import os
import fnmatch

for f_name in os.listdir('./'):
    if fnmatch.fnmatch(f_name, '*-*-python.ipynb'):
        print(f_name)
import os
import fnmatch

for f_name in os.listdir('./'):
    if fnmatch.fnmatch(f_name, '2'+'-*-*.*'):
        print(f_name)<jupyter_output>2-advanced-python.ipynb<jupyter_text>另一个有用的模式匹配模块是 glob。.glob() 在 glob 模块中的左右就像 fnmatch.fnmatch()，但是与 fnmach.fnmatch() 不同的是，它将以 . 开头的文件视为特殊文件。<jupyter_code>import glob

print(glob.glob('*.py'))<jupyter_output>['app.py', 'argv_argparse.py', 'globalLog.py', 'hello.py', 'mydict.py', 'mydict2.py', 'mydict_test.py', 'mymodule.py', 'my_calendar.py', 'server.py', 'simple.py', 'something.py', 'something_all.py']<jupyter_text>glob 还支持 shell 样式的通配符来进行匹配 :<jupyter_code>import glob

for name in glob.glob('*[0-9]*.ipynb'):
    print(name)<jupyter_output>1.1-basic-python.ipynb
1.2-basic-python.ipynb
1.3-basic-python.ipynb
2-advanced-python.ipynb<jupyter_text>glob 也很容易在子目录中递归的搜索文件:<jupyter_code>import glob

for name in glob.iglob('**/*.py', recursive=True):
    print(name)<jupyter_output>app.py
argv_argparse.py
globalLog.py
hello.py
mydict.py
mydict2.py
mydict_test.py
mymodule.py
my_calendar.py
server.py
simple.py
something.py
something_all.py<jupyter_text>glob.iglob() 在当前目录和子目录中搜索所有的 .py 文件。传递 recursive=True 作为 .iglob() 的参数使其搜索当前目录和子目录中的 .py 文件。glob.glob() 和 glob.iglob() 不同之处在于，iglob() 返回一个迭代器而不是一个列表。 如果要列出子目录而不是文件：<jupyter_code>import os

basepath = './'
for entry in os.listdir(basepath):
    if os.path.isdir(os.path.join(basepath, entry)):
        print(entry)
import os

basepath = './'
with os.scandir(basepath) as entries:
    for entry in entries:
        if entry.is_dir():
            print(entry.name)
from pathlib import Path

basepath = Path('./')
for entry in basepath.iterdir():
    if entry.is_dir():
        print(entry.name)<jupyter_output>.ipynb_checkpoints
pictures
test
__pycache__<jupyter_text>接下来，对文件进行简单操作，比如复制，移动，删除。注意，文件类型的操作还是比较容易引起系统级别的错误的（比如copy函数，当文件特别多的时候，循环使用它可能比较容易引起不提示的奇怪错误，比如文件系统崩溃等），所以还是尽量使用try，并且避免太大量的循环性质的操作。shutil 提供了一些复制文件的函数。 最常用的函数是 shutil.copy() 和 shutil.copy2() 。 使用shutil.copy() 将文件从一个位置复制到另一个位置，请执行以下操作：<jupyter_code>import shutil

src = './test.txt'
dst = './test1.txt'
shutil.copy(src, dst)<jupyter_output><empty_output><jupyter_text>移动文件或目录到指定位置可以使用：shutil.move(src，dst)，src 是要移动的文件或目录，dst 是目标。如果已经存在文件，仍然移动的话，就会报错。<jupyter_code>import shutil
dst = shutil.move('./test1.txt', 'test/')
print(dst)  # 'backup'<jupyter_output><empty_output><jupyter_text>接下来，记录关于文件归档和提取的操作。归档是将**多个文件打包成一个文件的便捷方式**。 两种最常见的存档类型是**ZIP和TAR**。 你编写的Python程序可以创建存档文件，读取存档文件和从存档文件中提取数据。这里记录下如何读取和写入两种压缩格式。首先是zip文件。先看看如何提取zip文件。zipfile 模块允许你通过 .extract() 和 .extractall() 从ZIP文件中提取一个或多个文件。这里直接看看如何递归提取，这里参考了：[How to extract zip file recursively in Python](https://stackoverflow.com/questions/36285502/how-to-extract-zip-file-recursively-in-python)<jupyter_code>import zipfile, re, os

def extract_nested_zip(zippedFile, toFolder):
    """ Extract a zip file including any nested zip files"""
    with zipfile.ZipFile(zippedFile, 'r') as zfile:
        zfile.extractall(path=toFolder)
    for root, dirs, files in os.walk(toFolder):
        print(root,dirs,files)
        for filename in files:
            if re.search(r'\.zip$', filename):
                fileSpec = os.path.join(root, filename)
                new_dir = os.path.join(root,filename[0:-4])
                extract_nested_zip(fileSpec, new_dir)
                
extract_nested_zip("test/test.zip","test/test")<jupyter_output>test/test ['1', 'test'] ['1 - Copy.txt', '1.txt', '1.zip']
test/test\1 [] ['1 - Copy (2).txt', '1 - Copy (3).txt']
test/test\1 [] ['1 - Copy (2).txt', '1 - Copy (3).txt']
test/test\test ['test'] []
test/test\test\test ['test'] []
test/test\test\test\test ['test'] []
test/test\test\test\test\test [] []<jupyter_text>电子邮件电子邮件的流程和传统邮件基本一致，假设自己的电子邮件地址是<EMAIL>，对方的电子邮件地址是<EMAIL>，梳理一下基本的流程：1. 用Outlook或者Foxmail之类的软件写好邮件，填上对方的Email地址，点“发送”，电子邮件就发出去了。这些电子邮件软件被称为**MUA：Mail User Agent——邮件用户代理**；2. Email从MUA发出去，不是直接到达对方电脑，而是发到**MTA：Mail Transfer Agent——邮件传输代理**，就是那些Email服务提供商，比如网易、新浪等等，本例中，Email首先被投递到网易提供的MTA，再由网易的MTA发到新浪的MTA；3. 到达新浪的MTA后，新浪的MTA会把Email投递到邮件的最终目的地**MDA：Mail Delivery Agent——邮件投递代理**，Email到达MDA后，就静静地躺在新浪的某个服务器上，存放在某个文件或特殊的数据库里，我们将这个长期保存邮件的地方称之为电子邮箱；4. 对方要取到邮件，必须通过MUA从MDA上把邮件取到自己的电脑上。所以，一封电子邮件的旅程就是：```发件人 -> MUA -> MTA -> MTA -> 若干个MTA -> MDA <- MUA <- 收件人```因此要编写程序来发送和接收邮件，本质上就是：- 编写MUA把邮件发到MTA；- 编写MUA从MDA上收邮件。发邮件时，MUA和MTA使用的协议就是**SMTP**：Simple Mail Transfer Protocol，后面的MTA到另一个MTA也是用SMTP协议。收邮件时，MUA和MDA使用的协议有两种：**POP**：Post Office Protocol，目前版本是3，俗称POP3；**IMAP**：Internet Message Access Protocol，目前版本是4，优点是不但能取邮件，还可以直接操作MDA上存储的邮件，比如从收件箱移到垃圾箱，等等。邮件客户端软件在发邮件时，会让你先**配置SMTP服务器**，也就是你要发到哪个MTA上。假设你正在使用163的邮箱，你就不能直接发到新浪的MTA上，因为它只服务新浪的用户，所以，你得填163提供的SMTP服务器地址：smtp.163.com，为了证明你是163的用户，SMTP服务器还要求你填写邮箱地址和邮箱口令，这样，MUA才能正常地把Email通过SMTP协议发送到MTA。类似的，从MDA收邮件时，MDA服务器也要求**验证你的邮箱口令**，确保不会有人冒充你收取你的邮件，所以，Outlook之类的邮件客户端会要求你填写POP3或IMAP服务器地址、邮箱地址和口令，这样，MUA才能顺利地通过POP或IMAP协议从MDA取到邮件。特别注意，目前大多数邮件服务商都需要手动打开SMTP发信和POP收信的功能。接下来以谷歌邮箱为例记录邮件发送相关内容，这部分主要参考：[Python邮件发送，看这篇就够](https://juejin.im/post/5c6d64c7f265da2dca385d90)。目前简单记录一些基本内容，详细内容可参考原文。这里使用Gmail，需要调整Gmail账户的[安全设置](https://myaccount.google.com/lesssecureapps)来允许python代码访问，这可能泄露登陆信息的，因此最好用一个全新的账号专门来测试。如果你不想降低Gmail帐户的安全设置，请查看Google的[文档](https://developers.google.com/gmail/api/quickstart/python)，了解如何使用Python脚本进行OAuth2授权来获取访问凭据。使用 SMTP_SSL() 建立安全的SMTP连接（下面是随意输入的账户密码，所以会报错，正常换成正确的账号密码就没问题）:<jupyter_code>import smtplib, ssl

port = 465  # For SSL
smtp_server = "smtp.gmail.com"
sender_email = "<EMAIL>"  # Enter your address
receiver_email = "<EMAIL>"  # Enter receiver address
password = input("Type your password and press enter: ")
message = """\
Subject: Hi there

This message is sent from Python."""

context = ssl.create_default_context()
with smtplib.SMTP_SSL(smtp_server, port, context=context) as server:
    server.login(sender_email, password)
    server.sendmail(sender_email, receiver_email, message)<jupyter_output>Type your password and press enter:  11<jupyter_text>Web开发关于web方面的基础只是可以直接参考：[HTTP协议简介](https://www.liaoxuefeng.com/wiki/1016959663602400/1017804782304672)等内容。这里就不多记录了，直接从python应用开始。一个Web应用的本质就是：1. 浏览器发送一个HTTP请求；2. 服务器收到请求，生成一个HTML文档；3. 服务器把HTML文档作为HTTP响应的Body发送给浏览器；4. 浏览器收到HTTP响应，从HTTP Body取出HTML文档并显示。接受HTTP请求、解析HTTP请求、发送HTTP响应这些底层代码都已经由专门的服务器软件实现，用python就专注于生成HTML文档即可，这需要一个统一的接口来帮我们屏蔽掉底层，这就是WSGI：Web Server Gateway Interface。WSGI接口定义非常简单，它只要求Web开发者实现一个函数，就可以响应HTTP请求。我们来看一个最简单的Web版本的“Hello, web!”：<jupyter_code>def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])  # 发送了HTTP响应的Header
    return [b'<h1>Hello, web!</h1>']  # HTTP响应的Body<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.15091710293397612, 'fraction_numerical': 0.028360513728296486, 'mean_word_length': 7.253652471246503, 'pattern_counts': {'":': 6, '<': 163, '<?xml version=': 0, '>': 166, 'https://': 13, 'lorem ipsum': 0, 'www.': 7, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '45', 'dir': 'jupyter-structured-clean-dedup', 'id': '84832', 'license': 'MIT', 'n_tokens_mistral': 15409, 'n_tokens_neox': 16195, 'n_words': 1392, 'path': '1-learn-python/3-python-library.ipynb', 'repo_name': 'forzhr/hydrus', 'size': '26574'}