完全掌握Python(七)

本文介绍python的标准库。python标准库是python自带的,无需手动安装。

想要了解python如何实现自动管理文件,自动处理zip、csv、json文件,了解sqlite的用法、电子邮件的发送,以及正确使用时间格式、随机成复杂密码,并使用命令行参数或运行外部程序,您只需阅读本文就能了解。


Paths

文件和目录的操作是程序开发中常用的场景,Path类可以很好地完成这个工作。

from pathlib import Path

Path(r”c:\Program Files\Microsoft”) 

# 使用绝对路径,前面加 r 表示原样,这样不用加转义字符了

Path(“/usr/local/bin”)

Path()  # 当前目录

Path(“ecommerce/__init__.py”)  # 相对路径

Path()/”ecommerce”/”__init__.py”  # 合并路径,结果与上一行代码一样

Path.home()  # 取得用户主目录

可以google搜索”python3 pathlib”查看详细 

继续:

path = Path(“ecommerce/__init__.py”)

print(path.exists())  # True

print(path.is_file())  # True

print(path.is_dir())  # False

print(path.stem)  # 获取不包含最后扩展名的文件名,__init__

print(path.suffix)  # 获取扩展名, .py

print(path.parent)  # ecommerce

path = path.with_name(“file.txt”)

# 将文件改名,仅生成对象,实际文件未作修改

path.absolute()  # 绝对路径

path = path.with_suffix(“.txt”)

# 修改扩展名,也是仅生成对象,实际文件未修改


目录操作

目录常用方法,看名字就显而易见。

path = Path(“ecommerce”)

print(path.exists())  # 查目录是否存在

path.mkdir()  # 创建目录

path.rmdir()  # 删除目录

path.rename(“ecommerce2”)  # 改名

目录遍历:

path = Path(“ecommerce”)

for p in path.iterdir():

print(p)

# iterdir方法产生生成器对象,之前在列表推导中提到到生成器对象

# 它不会全在内存中生成,用到时再生成,适合用于目录下有大量文件的情形下

# 如果目录下的文件和文件夹不多,可以使用列表推导生成列表

paths = [p for p in path.iterdir()]

print(paths)

我们看到输出是 PosixPath(‘ecommerce/shopping’)这样列表项,PosixPath表示是Linux文件。如果是 windows的话,则是 WindowsPath。

过滤只列出目录:

paths = [p for p in path.iterdir() if p.is_dir()]

iterdir()很好用,不过有局限:一是不能用模式搜索;二是不能递归,比如列出子文件夹下的文件。

可以用glob代替,下面的代码将列出目录下的所有py文件

py_files = [p for p in path.glob(“*.py”)]

glob不能递归,而 rglob则可以:

py_files = [p for p in path.rglob(“*.py”)]


文件操作

基本操作:

from pathlib import Path

from time import ctime

path = Path(“ecommerce/__init__.py”)

print(path.exists())

path.rename(“ecommerce/init.txt”)  # 修改文件名

path.unlink()  # 删除链接或文件

print(path.stat())

# 输出 文件信息

# os.stat_result(st_mode=33188, st_ino=15849392, st_dev=16777220, 

# st_nlink=1, st_uid=501, st_gid=20, st_size=0, st_atime=1653210238, 

# st_mtime=1653210238, st_ctime=1653210238)

# 我们可以转换成人类能读懂信息,比如创建时间st_ctime

print(ctime(path.stat().st_ctime))

读取文件:

path.read_bytes()

path.read_text()

# 如果用内置open函数会很麻烦,

# with open(“__init__.py”) as file:

#     …

# 而用read_bytes 和 read_text 这一切都自动完成。

写文件:

path.write_bytes(data)

path.write_text(“test”)

复制文件:

source = Path(“ecommerce/__init__.py”)

target = Path()/”__init__.py”

target.write_text(source.read_text())

这种方法稍显麻烦,可以使用shutil模块的copy方法。

import shutil

shutil.copy(source, target)

# 这种方法更干净高效


使用ZIP文件

打包写入zip文件:

from pathlib import Path

from zipfile import ZipFile

with ZipFile(“files.zip”, “w”) as zip:

    for path in Path(“ecommerce”).rglob(“*.*”):

        zip.write(path)

# 打包ecommerce文件夹为 files.zip

# rglob(“*.*”)表示递归获取所有文件夹和文件

# with 简化操作,否则要捕捉错误等操作

读zip文件:

from zipfile import ZipFile

with ZipFile(“files.zip”) as zip:

    print(zip.filelist)

info = zip.getinfo(“ecommerce/__init__.py”)

# 从上一步查看得到文件名

print(info.file_size)  # 查原文件大小

print(info.compress_size)  # 查压缩后大小

解压缩:

with ZipFile(“files.zip”) as zip:

    zip.extractall(“extract”)

# 提取所有文件到“extract”文件夹


使用CSV文件

写:

import csv

with open(“data.csv”, “w”) as file:

    writer = csv.writer(file)

    writer.writerow([“transaction_id”, “product_id”, “price”])

    writer.writerow([1000, 1, 5])

    writer.writerow([1001, 2, 15])

# 生成一个data.csv文件

读:

import csv

with open(“data.csv”) as file:

    reader = csv.reader(file)

    print(list(reader))

# 输出 [[‘transaction_id’, ‘product_id’, ‘price’], [‘1000’, ‘1’, ‘5’], [‘1001’, ‘2’, ’15’]]

# 每一项都是字符串,需要处理数字、浮点数的话,需要转换

或循环输出:

for row in reader:

    print(row)

# 如果已使用了 list(reader)读取,再执行该for循环将输出空,因为文件已读到结尾。


JSON转换

转成json格式:

import json

from pathlib import Path

movies = [

    {“id”: 1, “title”: “终结者”, “year”: 1989},

    {“id”: 2, “title”: “幼儿园特警”, “year”: 1993},

]

data = json.dumps(movies)  # 输出json格式

Path(“movies.json”).write_text(data)  # 保存 movies.json文件

加载json,转回ptyhon的数据结构:

import json

from pathlib import Path

data = Path(“movies.json”).read_text()

movies = json.loads(data)

# 返回字典列表

print(movies)


使用SQLite数据库

SQLite是一个简单的数据库。

可以 google搜索  db browser for sqlite,下载相应的工具

新建db.sqlite3文件,并创建表movies,字段分别为it,title,year。

写数据库:

import sqlite3

import json

from pathlib import Path

movies = json.loads(Path(“movies.json”).read_text())

# 读取json文件并存储到字典列表 movies中

with sqlite3.connect(“db.sqlite3”) as conn:

    command = “INSERT INTO movies VALUES(?,?,?)”

    for movie in movies:

        conn.execute(command, tuple(movie.values()))

        # tuple函数是将Values变成元组

    conn.commit

    # 事先要建好movies表,否则出错


读数据库:

with sqlite3.connect(“db.sqlite3”) as conn:

    command = “SELECT *FROM movies”

    cursor = conn.execute(command)

    # 游标curson是可迭代对象,使用for循环获取各条记录

    for row in cursor:

        print(row)

    # 输出结果是:

    # (1, ‘终结者’, 1989)

    # (2, ‘幼儿园特警’, 1993)

    # for语句也可改成用fetchall获取

    movies = cursor.fetchall()

    print(movies)

    # 输出 [(1, ‘终结者’, 1989), (2, ‘幼儿园特警’, 1993)]


使用时间戳 time

python中处理时间有2个模块:time和datetime。

time是一个时间戳一串数字,从1970-1-1年开始计算秒的总数;datetime则是有有年份、月份、日期等。

本小节先讲time。示例计算程序执行时间。

import time

def send_emails():

    for i in range(10000):

        pass

start = time.time()

send_emails()

end = time.time()

duration = end-start

print(duration)

# 类似于timeit的功能,前面讲异常时提到过

# 在我的电脑输出是 0.0003070831298828125


使用datetime

from datetime import datetime

import time

dt1 = datetime(2022, 1, 1)

dt2 = datetime.now()

dt = datetime.strptime(“2022/01/01”, “%Y/%m/%d”)

# 将字符串改变成时间对象

dt = datetime.fromtimestamp(time.time())

# 时间戳转成时间对象

print(f”{dt.year}/{dt.month}/{dt.day}”)

# 格式化输出2022/5/23

print(dt.strftime(“%Y/%m/%d”))

# strftime用于将时间对象转成字符串,输出 2022/05/23

# 比较

print(dt2 > dt1)  # True


使用时间增量 timedelta

from datetime import datetime, timedelta

dt1 = datetime(2022, 1, 1)+timedelta(days=1, seconds=1000)

print(dt1)

# 输出 2022-01-02 00:16:40

dt2 = datetime.now()

duration = dt2-dt1

print(duration)

# 输出 141 days, 17:53:04.472024

print(“days”, duration.days)  # 天数

print(“seconds”, duration.seconds)  # 除天数之后的秒数

print(“total_seconds”, duration.total_seconds())  # 折算成总秒数


产生随机值

import random

import string

print(random.random())  # 产生0-1之间的一个随机数

print(random.randint(1, 10))  # 产生1-10之间的一个整数

print(random.choice([1, 2, 3, 4]))  # 选择一个数

print(random.choices([1, 2, 3, 4], k=2))  # 随机选择2个数

print(

    “,”.join(random.choices(“abcdefghi”, k=4))

)

# 随机生成a-i之间的一个密码,用 , 连接

print(

    “”.join(random.choices(string.ascii_letters +

            string.digits + string.punctuation, k=12))

)

# 随机生成一个12位的强密码

# 随机改变顺序

numbers = [1, 2, 3, 4]

random.shuffle(numbers)

print(numbers)


打开浏览器

import webbrowser

webbrowser.open(“https://www.baidu.com”)


发送电子邮件

from email.mime.multipart import MIMEMultipart

from email.mime.text import MIMEText

from email.mime.image import MIMEImage

from pathlib import Path

import smtplib

message = MIMEMultipart()

message[“from”] = “kelemi001@qq.com”

message[“to”] = “kelemi@zj.gov.cn”

message[“subject”] = “This is a test.”

message.attach(MIMEText(“Body”, “plain”))

# plain表示纯文本,也可以用”html”表示html

message.attach(MIMEImage(Path(“kelemi.jpeg”).read_bytes()))

# 附加了图片

with smtplib.SMTP_SSL(‘smtp.qq.com’, 465) as smtp:

    smtp.login(“kelemi001@qq.com”, “bjepgdcwjrkjbidjikkkkkkggg”)

    smtp.send_message(message)

    print(“Sent…”)

上述方法一开始就创建安全连接SSL,端口用465。有些先进的邮箱支持先创建不安全连接,后续再创建安全连联,如gmail,可以使用不加密的smtplib.smtp方法,后再调用加密starttsl。

with smtplib.SMTP(host=’smtp.gmail.com’, port=587) as smtp:

# 开始用smtp方法不加密,gmail端口是587

    smtp.ehlo()  # 先询问

    smtp.starttls()  # 再加密

    smtp.login(“kelemi@gmail.com”, “my_password”)

    smtp.send_message(message)

    print(“Sent…”)


模板

创建一个template.html文件:

在vscode中,输入!再按tab,自动生成html框架,再在body部分输入:

Hi $name, this is a test.

注意美元符号开头表示变量。template.html内容如下:

<!DOCTYPE html>

<html lang=”en”>

    <head>

        <meta charset=”UTF-8″ />

        <meta http-equiv=”X-UA-Compatible” content=”IE=edge” />

        <meta name=”viewport” content=”width=device-width, initial-scale=1.0″ />

        <title>Document</title>

    </head>

    <body>

        Hi $name, this is a test.

    </body>

</html>

再在代码中替换变量:

from string import Template

from pathlib import Path

template = Template(Path(“template.html”).read_text())

print(

    template.substitute({“name”: “kelemi”})

)

# 模板中的name将由kelemi代替

# 除了使用字典,也可传递关键字参数,效果是一样的。

print(

    template.substitute(name=”kelemi”)

)


命令行参数

sys.argv可以查看命令行参数。

import sys

print(sys.argv)

# 如果执行 python3 -a -b -c

# 输出 [‘app.py’, ‘-a’, ‘-b’, ‘-c’]

# 第一个列表项是应用程序本身

来看个简单的示例,要求用户提供密码参数。

import sys

if len(sys.argv) == 1:

print(“USAGE:python3 app.py <password>”)

else:

password = sys.argv[1]

print(“Password”, password)


运行外部程序

运行外部程序并获得输出,这对自动化任务是有用的。用的是subprocess模块,使用subprocess.run()方法。有些python教程使用的subprocess模块的其它方法如call,check_all,check_output,popen等方法一般不用了,属过时的方法。

import subprocess

completed = subprocess.run([“ls”, “-l”])

# 终端将显示输出

print(completed.args)  # [‘ls’, ‘-l’]

print(completed.returncode)  # 0

print(completed.stderr)  # None

print(completed.stdout)  # 没捕捉显示为None

有时候需要获取输出并进行下一步操作,刚设置 capture_output为True。

completed = subprocess.run([“ls”, “-l”], capture_output=True)

# capture_output=True表示捕捉标准输出,终端不再显示

print(completed.stdout)

# 能看到已捕捉输出,b’total 112\n…

# 前面是二进制输出,前面有个b 

# 如果需要文本输出,传递关键字参数 text=True 即可

completed = subprocess.run([“ls”, “-l”], capture_output=True, text=True)

运行另一个python 脚本:

completed = subprocess.run([“python3”, “other.py”],

    capture_output=True, text=True)

# 运行另外一个python程序,属于另外一个进程,不同内存空间,与import是不同的

异常捕捉:

执行外部命令如果返回非0,且参数check=True的话,会抛出异常需要进行捕捉。

import subprocess

try:

    completed = subprocess.run(

            [“false”], capture_output=True, text=True, check=True

            # check=True 在返回非0的情况下将抛出异常

    )

except subprocess.CalledProcessError as ex:

    print(ex)

# 输出 Command ‘[‘false’]’ returned non-zero exit status 1.


小结

本文介绍了python的标准库常用的使用场景,包括文件、目录的操作,ZIP文件、csv文件、json文件的使用方法,sqlite的使用,时间,随机数,浏览器的打开,发送电子邮件,模板用法,以及命令行参数及运行外部程序,内容非常丰富。

下一节计划讲解python包的安装、发布以及依赖管理,并介绍python虚拟环境的搭建。近段时间事情较多,更新可能会放慢,敬请期待。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注