0%

Mosh的课程网址

Web Scraping

1
2
3
4
5
6
7
8
9
10
11
12
13
import requests
from bs4 import BeautifulSoup

response = requests.get("https://stackoverflow.com/questions")
soup = BeautifulSoup(response.text, "html.parser")

questions = soup.select(".question-summary")
print(questions[0].attrs)
print(questions[0].get("id", 0))

for question in questions:
print(question.select_one(".question-hyperlink").getText())
print(question.select_one(".vote-count-post").getText())

{‘class’: [‘question-summary’], ‘id’: ‘question-summary-67532680’}
question-summary-67532680
GKE Nginx Ingress Controller Oauth2 Proxy redirect
0
Workaround on nested async completion blocks from network calls? Without using PromiseKit
0
ax.text not being printed when using transform
0
Using html & javascript Populate textbox
0
How to pass JSON data through the Django backend to frontend view using Angular
0
Gitlab CI cannot pull private registry with DOCKER_AUTH_CONFIG
0
Shopify SKU Lookup using GraphQL
0

Browser Automation

自动打开chrome,跳转到github.com,登录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from selenium import webdriver
import time

browser = webdriver.Chrome()
browser.set_window_size(1920, 1080) # 设置窗口大小

browser.get("https://github.com")

signin_link = browser.find_element_by_link_text("Sign in")
signin_link.click()

username_box = browser.find_element_by_id("login_field")
username_box.send_keys("PurpleMStone")
password_box = browser.find_element_by_id("password")
password_box.send_keys("zq15071452268")
password_box.submit()

assert "PurpleMStone" in browser.page_source

time.sleep(0.5)

# profile_link = browser.find_element_by_class_name("user-profile-link")
# link_label = profile_link.get_attribute("innerHTML")
# assert "PurpleMStone" in link_label

browser.quit()

Working with PDFs

Demo 1

1
2
3
4
5
6
7
8
9
10
11
12
import PyPDF2

with open("first.pdf", "rb") as file:
reader = PyPDF2.PdfFileReader(file)
print(reader.numPages)
page = reader.getPage(0) # 获取第一页
page.rotateClockwise(90) # 页面旋转90度

writer = PyPDF2.PdfFileWriter()
writer.addPage(page)
with open("rotated.pdf", "wb") as output:
writer.write(output)

Demo 2: 合并PDF

1
2
3
4
5
6
7
import PyPDF2

merger = PyPDF2.PdfFileMerger()
file_names = ["first.pdf", "second.pdf"]
for file_name in file_names:
merger.append(file_name)
merger.write("combined.pdf")

Excel Spreadsheets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import openpyxl

wb = openpyxl.load_workbook("transactions.xlsx")
print(wb.sheetnames)

sheet = wb["Sheet1"]

# wb.create_sheet(["Sheet2", 0])
# wb.remove_sheet(sheet)

cell = sheet["a1"]
# print(cell.row)
# print(cell.column)
# print(cell.coordinate)
# cell = sheet.cell(row=1, column=1)

print(sheet.max_row)
print(sheet.max_column)

for row in range(1, sheet.max_row + 1):
for column in range(1, sheet.max_column + 1):
cell = sheet.cell(row, column)
# print(cell.value)

column = sheet["a"]
print(column)
cells = sheet["a:c"] # column
print(cells)

print(sheet[1:3]) # row

sheet.append([1, 2, 3]) # add a row at the end of the sheet
# sheet.insert_rows insert_columns, delete_rows, delete_columns

wb.save("transaction2.xlsx")

Numpy

1
2
3
4
5
6
import numpy as np

array = np.array([[1, 2, 3], [4, 5, 6]])
print(array)
print(type(np.array))
print(array.shape)
1
2
3
4
5
6
7
8
array = np.zeros((3, 4), dtype=int)
print(array)

array = np.ones((3, 4), dtype=int)
print(array)

array = np.full((3, 4), 5, dtype=int)
print(array)
1
2
3
4
5
6
7
8
9
10
11
12
array = np.random.random((3, 4))
print(array)
print(array[0, 0])

print(array > 0.2)

print(array[array > 0.2])

print(np.sum(array))
print(np.floor(array))
print(np.ceil(array))
print(np.round(array))
1
2
3
first = np.array([1, 2, 3])
second = np.array([1, 2, 3])
print(first + second)

Mosh的课程网址

Python标准库不一定够用。所以有:

pypi官网

Pip

1
2
3
4
5
6
pip install requests				(安装包)
pip list (查看已安装列表)
pip install requests==2.9.0 (安装早前版本的包)
pip install requests==2.9.* (安装早前版本中符合条件的最近的包)
pip install requests~=2.9.0 (同上)
pip uninstall requests (卸载)

使用

1
2
3
4
import requests

response = requests.get("https://baidu.com")
print(response)

输出:

<Response [200]>

200表示成功。

Virtual Environments

创建虚拟环境:

1
> python -m venv env

可以看到当前目录下有一个env文件夹,里面有个pyvenv.cfg,内容如下:

home = D:\Python39
include-system-site-packages = false
version = 3.9.4

进入虚拟环境:

1
> env\Scripts\activate.bat

退出虚拟环境:

1
> deactivate

Pipenv

先安装:

1
> pip install pipenv

查看虚拟环境在哪(一般不在当前工作目录下,为了节省空间):

1
> pipenv --venv

进入虚拟环境:

1
> pipenv shell

退出虚拟环境:

1
> exit

Virtual Environments in VSCode

查看虚拟环境在哪:

1
> pipenv --venv

得到路径: C:/Users/11405/.virtualenvs/HelloWorldBjQZ8Ory

在VSCode中,File→Preference→settings,打开settings.json,改这两项(没有就自己加):

1
2
3
4
"python.pythonPath": "C:/Users/11405/.virtualenvs/HelloWorldBjQZ8Ory/Scripts/python.exe",
"code-runner.executorMap": {
"python": "C:/Users/11405/.virtualenvs/HelloWorldBjQZ8Ory/Scripts/python.exe",
}

点击VSCode左下角,然后选择pipenv为解释器,即可。

Pipfile

在用pipenv创建虚拟环境时,当前目录(不是虚拟环境所在目录)下会生成两个文件:

Pipfile

Pipfile.lock

Pipfile文件:

[[source]]
url = “https://pypi.org/simple
verify_ssl = true
name = “pypi”

[packages]
requests = “*”

[dev-packages]
pylint = “*”

[requires]
python_version = “3.9”

Pipfile.lock是一个json文件,存了所有包的版本,所以换一台机器跑这个代码,依赖的还是这些版本的包,就不会出问题。

在另一台机器,只要:

1
> pipenv install

就会根据Pipfile创建一样的环境

1
> pipenv install --ignore-pipfile

就会根据Pipfile.lock安装依赖包。

Managing the Dependencies

可以用

1
> pipenv graph

看已安装的包及其依赖项。

卸载某个包后,再用上述命令查看,会发现依赖项还在(因为这些依赖项可能在别的地方有用的)。

更新包和依赖项(想安装最新的,就改Pipfile中的版本号为*):

1
> pipenv update requests

更新所有包的依赖项:

1
> pipenv update --outdated

Publishing Packages

在pypi官网上注册一个账号(记得激活邮箱)。

然后安装:

1
> pip install setuptools wheel twine

新建一个项目,包含 stonepdf,test(单元测试),data(各种数据) 三个文件夹。

需要以下文件:

  1. LICENSE:到 https://choosealicense.com/ copy一个

  2. README.md:介绍你的项目

  3. setup.py

1
2
3
4
5
6
7
8
9
10
11
# setup.py
import setuptools
import setuptools
from pathlib import Path

setuptools.setup(
name="stonepdf",
version=1.0,
long_description=Path("README.md").read_text(),
packages=setuptools.find_packages(exclude=["tests", "data"]) # 忽略这两个
)

然后运行(sdist表示source distribution, bdist表示built distribution):

1
> python setup.py sdist bdist_wheel

然后上传:

1
> twine upload dist/*

*表示上传dist目录下所有文件。

然后就可以像用其他包一样用我们的包啦。

Docstrings

其他人提供的module在VSCode中有许多提示性信息显示出来,这就需要Docstrings。

比如(用””” “””包含的部分):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
""" This module provides functions to convert a PDF to text. """


class Converter:
""" A simple converter for converting PDFs to text. """

def convert(self, path):
"""
Convert the given PDF to text.

Parameters:
path (str): The path to a PDF file.

Returns:
str: The content of the PDF file as text.
"""

print("pdf2text")

这些信息在 VSCode 调用这些 module 的 method、class 时会显示出来,便于别人使用。

003

Pydoc

1
2
3
4
> pydoc math
> pydoc stonepdf.pdf2text
> pydoc -w stonepdf.pdf2text
> pydoc -p 1234

查看包的介绍文件。-w在html中显示。-p会提供网址。

在 Windows 用:

1
> python -m pydoc math

其余同理。

Mosh的课程网址

Files

Paths

Demo 1: Basic Methods

1
2
3
4
5
6
7
8
9
10
from pathlib import Path

path = Path(r"ecommerce/__init__.py") # create a path object
print(path.exists()) # file exists or not
print(path.is_file()) # is a file or not
print(path.is_dir()) # is a directory or not
print(path.name)
print(path.stem) # return the file name without extension
print(path.suffix) # return the extension
print(path.parent) # parent folder

输出结果:

True
True
False
__init__.py
__init__
.py
ecommerce

Demo 2

1
2
3
4
5
6
7
8
9
10
11
from pathlib import Path

path = Path(r"ecommerce/__init__.py") # create a path object

path = path.with_name("file.txt") # 产生一个新path object,(文件尚未产生)
print(path)

print(path.absolute())

path = path.with_suffix(".txt") # 产生一个新path object,(文件尚未产生)
print(path)

输出:

ecommerce\file.txt
e:\Stone\Python\HelloWorld\ecommerce\file.txt
ecommerce\file.txt

Working with Directories

Demo 1: Basic Methods

1
2
3
4
5
6
7
from pathlib import Path

path = Path("ecommerce")
path.exists()
path.mkdir() # creat this directory
path.rmdir() # remove this directory
path.rename("ecommerce2") # rename

Demo 2

1
2
3
4
from pathlib import Path

path = Path("ecommerce")
print(path.iterdir())

输出:

<generator object Path.iterdir at 0x00000180577926D0>

可见,得到的是一个generator object。因为目录下可能有大量文件,generator 更高效。

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

输出:

[WindowsPath(‘ecommerce/customer’), WindowsPath(‘ecommerce/shopping’), WindowsPath(‘ecommerce/__init__.py’), WindowsPath(‘ecommerce/__pycache__‘)]

1
2
3
# filter: 只获取目录
paths = [p for p in path.iterdir() if p.is_dir()]
print(paths)

输出:

[WindowsPath(‘ecommerce/customer’), WindowsPath(‘ecommerce/shopping’), WindowsPath(‘ecommerce/__pycache__‘)]

1
2
3
# 当前目录下符合条件的文件
py_files = [p for p in path.glob("*.py")]
print(py_files)

输出:

[WindowsPath(‘ecommerce/__init__.py’)]

1
2
3
# recursively (包括子目录下符合条件的文件)
py_files = [p for p in path.rglob("*.py")]
print(py_files)

输出:

[WindowsPath(‘ecommerce/__init__.py’), WindowsPath(‘ecommerce/customer/contact.py’), WindowsPath(‘ecommerce/customer/__init__.py’), WindowsPath(‘ecommerce/shopping/sales.py’), WindowsPath(‘ecommerce/shopping/__init__.py’)]

Working with Files

Demo 1

1
2
3
4
5
6
from pathlib import Path

path = Path("ecommerce/__init__.py")
path.exists()
path.rename("init.txt")
path.unlink() # delete the file

Demo 2: 查看文件状态

1
2
3
from pathlib import Path

print(path.stat())

输出:

os.stat_result(st_mode=33206, st_ino=1688849860483389, st_dev=971387, st_nlink=1, st_uid=0, st_gid=0, st_size=32, st_atime=1620876102, st_mtime=1620816590, st_ctime=1620814908)

将时间转为容易识别的形式:

1
2
3
from time import ctime

print(ctime(path.stat().st_ctime)) # 打印文件的新建时间

输出:

Wed May 12 18:21:48 2021

Demo 3

1
2
3
4
print(path.read_bytes())
print(path.read_text())
path.write_bytes("...")
path.write_text("...")

输出:

b’print(“Ecommerce initialized”)\r\n’
print(“Ecommerce initialized”)

Demo 4:拷贝文件

1
2
3
4
5
6
7
8
from pathlib import Path
import shutil

source = Path("ecommerce/__init__.py")
target = Path() / "__init__.py"

# target.write_text(source.read_text()) # 简单粗暴:读->写
shutil.copy(source, target)

Working with Zip Files

Demo 1

1
2
3
4
5
6
7
from pathlib import Path
from zipfile import ZipFile

zip = ZipFile("files.zip", "w") # 生成一个zip文件
for path in Path("ecommerce").rglob("*.*"):
zip.write(path) # 将这些文件存到zip文件中
zip.close() # 关闭

Simpler way

1
2
3
4
5
6
from pathlib import Path
from zipfile import ZipFile

with ZipFile("files.zip", "w") as zip:
for path in Path("ecommerce").rglob("*.*"):
zip.write(path)

不需要手动close释放。

Demo 2

1
2
3
4
5
6
7
8
9
from pathlib import Path
from zipfile import ZipFile

with ZipFile("files.zip") as zip:
# print(zip.namelist()) # zip中的文件列表
info = zip.getinfo("ecommerce/__init__.py") # 获取文件信息
print(info.file_size) # 文件大小
print(info.compress_size) # 压缩大小
zip.extractall("extract") # 解压到extract文件夹

Working with CSV Files

Demo 1: 写csv

1
2
3
4
5
6
7
import csv

with open("data.csv", "w", newline='') as file:
writer = csv.writer(file)
writer.writerow(["transaction_id", "product_id", "price"])
writer.writerow([1000, 1, 5])
writer.writerow([1001, 2, 15])

如果不加newline=’’,文件会多空行

Demo 2: 读csv

1
2
3
4
5
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’]]

1
2
3
4
5
6
import csv

with open("data.csv") as file:
reader = csv.reader(file)
for row in reader: # reader is iterable
print(row)

输出:

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

Working with JSON Files

Demo 1: write JSON files

1
2
3
4
5
6
7
8
9
10
import json
from pathlib import Path

movies = [
{"id": 1, "title": "Terminator", "year": 1989},
{"id": 2, "title": "Kindergarten Cop", "year": 1993},
]

data = json.dumps(movies)
Path("movies.json").write_text(data)

Demo 2: read JSON Files

1
2
3
4
5
6
import json
from pathlib import Path

data = Path("movies.json").read_text()
movies = json.loads(data)
print(movies[0])

输出:

{‘id’: 1, ‘title’: ‘Terminator’, ‘year’: 1989}

Working with a SQLite Database

Demo 1: write

1
2
3
4
5
6
7
8
9
10
11
import sqlite3
import json
from pathlib import Path

movies = json.loads(Path("movies.json").read_text())

with sqlite3.connect("db.sqlite3") as conn:
command = "INSERT INTO Movies VALUES(?, ?, ?)"
for movie in movies:
conn.execute(command, tuple(movie.values()))
conn.commit()

Demo 2: read

1
2
3
4
5
6
7
import sqlite3

with sqlite3.connect("db.sqlite3") as conn:
command = "SELECT * FROM Movies"
cursor = conn.execute(command)
for row in cursor:
print(row)

或:

1
2
3
4
5
6
7
import sqlite3

with sqlite3.connect("db.sqlite3") as conn:
command = "SELECT * FROM Movies"
cursor = conn.execute(command)
movies = cursor.fetchall()
print(movies)

Time

Working with Timestamps

1
2
3
4
5
6
7
8
9
10
11
12
13
import time


def send_emails():
for i in range(1000000):
pass


start = time.time()
send_emails()
end = time.time()
duration = end - start
print(duration)

输出:

0.016989707946777344

Working with DateTimes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from datetime import datetime
import time

dt1 = datetime(2018, 1, 1)
print(dt1) # 2018-01-01 00:00:00
dt2 = datetime.now()
print(dt2) # 2021-05-13 16:51:52.269074
print(dt2 > dt1) # True

dt = datetime.strptime("2018/01/01", "%Y/%m/%d") # 指定时间
print(dt) # 2018-01-01 00:00:00

dt = datetime.fromtimestamp(time.time())
print(f"{dt.year}/{dt.month}") # 2021/5
print(dt.strftime("%Y/%m")) # 2021/05 (当地时间)

Working with Time Deltas

1
2
3
4
5
6
7
8
9
10
11
from datetime import datetime, timedelta

dt1 = datetime(2020, 1, 1) + timedelta(days=1, seconds=1000)
print(dt1)
dt2 = datetime.now()

duration = dt2 - dt1
print(duration)
print("days", duration.days)
print("seconds", duration.seconds) # 不计天数
print("total_seconds", duration.total_seconds()) # 计天数

输出:

2020-01-02 00:16:40
497 days, 16:44:06.578196
days 497
seconds 60246
total_seconds 43001046.578196

Random Values

Generating Random Values

Demo 1

1
2
3
4
5
6
7
8
import random
import string

print(random.random())
print(random.randint(1, 10))
print(random.choice([1, 2, 3, 4])) # 从给定的值中随机选一个
print(random.choices([1, 2, 3, 4], k=4)) # 从给定的值中随机选出序列
print("".join(random.choices("abcdefg", k=4)))

输出:

0.9791978038762406
2
3
[4, 1, 3, 3]
eefe

Demo 2

1
2
3
4
5
6
import random
import string

print(string.ascii_letters) # 所有字母
print(string.digits) # 所有数字
print("".join(random.choices(string.ascii_letters + string.digits, k=4)))

输出:

abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
0123456789
g02V

Demo 3:打乱顺序

1
2
3
4
5
import random

numbers = [1, 2, 3, 4]
random.shuffle(numbers)
print(numbers)

输出:

[4, 2, 3, 1]

Browser

Opening the Browser

1
2
3
4
import webbrowser

print("Deployment completed")
webbrowser.open("https://baidu.com")

Sending Emails

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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"] = "name_of_sender"
message["to"] = "email_of_recipients@email.com"
message["subject"] = "This is a test"
message.attach(MIMEText("Body")) # 正文
message.attach(MIMEImage(Path("ww.jpg").read_bytes())) # 附件图片

with smtplib.SMTP(host="smtp.whu.edu.cn", port=25) as smtp:
smtp.ehlo() # say hello to smtp server
smtp.starttls() # TLS
smtp.login("email_of_sender@email.com", "password")
smtp.send_message(message)
print("Sent...")

Working with Templates

新建一个html文件 template.html,输入!回车会自动生成模板,删除head块中的内容,body块写入内容。最后如下:

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en">

<head>
</head>

<body>
Hi <strong>$name</strong>, this is our test email.
</body>

</html>

strong表示字体加粗。

在上述email例子中:

1
2
3
from string import Template

template = Template(Path("template.html").read_text()) # 读入模板
1
2
3
# 相应语句改为
body = template.substitute({"name": "John"}) # 或者name="John"
message.attach(MIMEText(body, "html")) # "html"

即可。

Command-line Arguments

Demo 1

1
2
3
import sys

print(sys.argv)

>python app.py -a -b -c

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

Demo 2

1
2
3
4
5
6
7
import sys

if len(sys.argv) == 1:
print("USAGE: python3 app.py <password>")
else:
password = sys.argv[1]
print("password", password)

> python app.py

USAGE: python3 app.py <password>

> python app.py 1234

password 1234

Running External Programs

Python执行命令

在 window 10 上跑的。

用python 执行windows命令dir

1
2
3
4
5
6
7
import subprocess

completed = subprocess.run(["dir", "."],
shell=True,
capture_output=True,
text=True)
print(type(completed))

capture_output=True使得输出在completed.stdout。

输出:

<class ‘subprocess.CompletedProcess’>

1
2
3
4
print("args", completed.args)
print("returncode", completed.returncode) # 0: success
print("stderr", completed.stderr) # standard error
print("stdout", completed.stdout) # standard output

输出:

> python app.py

args [‘dir’, ‘.’]
returncode 0
stderr
stdout 驱动器 E 中的卷没有标签。
卷的序列号是 000E-D27B

E:\Stone\Python\HelloWorld 的目录

2021/05/13 19:24 <DIR> .
2021/05/13 19:24 <DIR> ..
2021/05/06 16:44 <DIR> .vscode
2021/05/13 19:24 341 app.py
2021/05/06 16:15 8 content.txt
2021/05/13 15:36 54 data.csv
2021/05/13 15:55 0 db.sqlite3
2021/05/12 18:49 <DIR> ecommerce
2021/05/13 15:27 <DIR> extract
2021/05/13 14:42 3,647 files.zip
2021/05/13 15:48 102 movies.json
2021/05/13 18:03 138 template.html
2021/05/13 17:42 78,556 ww.jpg
2021/05/12 18:12 <DIR> __pycache__
8 个文件 82,846 字节
6 个目录 46,196,027,392 可用字节

Python执行另一个python脚本

1
2
# other.py
print("Here is a complicated script.")
1
2
3
4
5
6
7
8
9
10
11
# app.py
import subprocess

completed = subprocess.run(["python", "other.py"],
shell=True,
capture_output=True,
text=True)
print("args", completed.args)
print("returncode", completed.returncode) # 0: success
print("stderr", completed.stderr) # standard error
print("stdout", completed.stdout) # standard output

> python app.py

args [‘python’, ‘other.py’]
returncode 0
stderr
stdout Here is a complicated script.

看到 stdout 就是 other.py 的输出。

错误命令

1
2
3
4
5
6
7
8
9
10
import subprocess

completed = subprocess.run(["False"],
shell=True,
capture_output=True,
text=True)
print("args", completed.args)
print("returncode", completed.returncode) # 0: success
print("stderr", completed.stderr) # standard error
print("stdout", completed.stdout) # standard output

> python app.py

args [‘False’]
returncode 1
stderr ‘False’ 不是内部或外部命令,也不是可运行的程序
或批处理文件。

stdout

1
2
if completed.returncode != 0:		# 可以这样判断处理
print(completed.stderr)

也可以这样处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import subprocess

try:
completed = subprocess.run(["False"],
shell=True,
capture_output=True,
text=True,
check=True) # 自动检查
print("args", completed.args)
print("returncode", completed.returncode) # 0: success
print("stderr", completed.stderr) # standard error
print("stdout", completed.stdout) # standard output

except subprocess.CalledProcessError as ex:
print(ex)

输出:

Command ‘[‘False’]’ returned non-zero exit status 1.

Mosh的课程网址

1
2
3
4
5
6
7
# sales.py
def calc_tax():
pass


def calc_shipping():
pass
1
2
3
4
5
# app.py
from sales import calc_shipping, calc_tax

calc_shipping()
calc_tax()

或:

1
2
3
4
import sales

sales.calc_shipping()
sales.calc_tax()

Compiled Python Files

编译后在__pycache__生成 sales.cpython-39.pyc 文件,如果sales.py未作更改,则此文件直接用(相当于缓存),可以加快程序执行。如果sales.py改了,则要重新生成此文件。

Module Search Path

1
2
3
import sys

print(sys.path)
1
['e:\\Stone\\Python\\HelloWorld', 'D:\\Python39\\python39.zip', 'D:\\Python39\\DLLs', 'D:\\Python39\\lib', 'D:\\Python39', 'C:\\Users\\11405\\AppData\\Roaming\\Python\\Python39\\site-packages', 'D:\\Python39\\lib\\site-packages']

Packages

package 对应于文件夹;module 对应于文件

比如在 app.py 所在目录下有个 ecommerce 文件夹,ecommerce 文件夹内有多个文件,包括 sales.py,假如改文件夹内有文件__init__.py,则 python 会将此文件夹视为 package.

那么可以这样用:

1
2
3
4
5
# app.py
from ecommerce import sales

sales.calc_tax()
sales.calc_shipping()

Sub-packages

ecommerce 文件夹内有 shopping 文件夹, shopping 文件夹内有文件__init__.py 和 sales.py,则:

1
2
# app.py
from ecommerce.shopping import sales

Intra-package References

002

1
2
3
4
5
# sales.py
from ecommerce.customer import contact
from ..customer import contact # ..表示父目录

contact.contact_customer()

The dir() Function

1
2
3
4
# app.py
from ecommerce.shopping import sales

print(dir(sales))

输出sales的各种方法:

1
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'calc_shipping', 'calc_tax', 'contact']

比如:

1
2
3
4
5
6
# app.py
from ecommerce.shopping import sales

print(sales.__name__)
print(sales.__package__)
print(sales.__file__)

输出:

1
2
3
ecommerce.shopping.sales
ecommerce.shopping
e:\Stone\Python\HelloWorld\ecommerce\shopping\sales.py

Executing Modules as Scripts

1
2
3
4
5
6
7
8
9
10
# ecommerce/shopping/sales.py
print("Sales initialized", __name__)


def calc_tax():
pass


def calc_shipping():
pass
1
2
# ecommerce/__init__.py
print("Ecommerce initialized")
1
2
# app.py
from ecommerce.shopping import sales

执行 app.py,输出:

1
2
Ecommerce initialized
Sales initialized ecommerce.shopping.sales

如果执行sales.py, 输出:

1
Sales initialized __main__

可见__name__对于不同py文件是不同的。

如果加上:

1
2
if __name__ == "__main__":
# bla bla bla

则只在当前py文件运行时执行这些 # blablabla 的code。

Mosh的课程网址

👉 Class: blueprint for creating new objects — Human

👉 Object: instance of a class — John, Mary, Jack

Creating Classes

1
2
3
4
5
6
7
8
9
class Point:
def draw(self):
print("draw")


point = Point()
print(type(point))

print(isinstance(point, Point)) # 判断point是不是Point的实例

输出结果:

1
2
<class '__main__.Point'>
True

Constructors

1
2
3
4
5
6
7
8
9
10
11
12
class Point:
def __init__(self, x, y): # Constructor
self.x = x
self.y = y

def draw(self):
print(f"Point ({self.x}, {self.y})")


point = Point(1, 2)
print(point.x)
point.draw()

self 指向当前 working 的 object, 比如 draw 不需要显式写 point.draw(point), 编译器会自动将当前的 object point 作为 draw() 的参数。

输出:

1
2
1
Point (1, 2)

Class vs Instance Attributes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Point:
default_color = "red" # class attribute

def __init__(self, x, y):
self.x = x # object/instance attribute
self.y = y

def draw(self):
print(f"Point ({self.x}, {self.y})")


point = Point(1, 2)
point.draw() # x和y是object attribute,每个object不同

another = Point(3, 4)
another.draw()

Point.default_color = "yellow" # 直接用类调用更改。类中所有实例共享
print(point.default_color)
print(Point.default_color)
print(another.default_color)

输出:

1
2
3
4
5
Point (1, 2)
Point (3, 4)
yellow
yellow
yellow

Class vs Instance Methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Point:
def __init__(self, x, y):
self.x = x
self.y = y

@classmethod
def zero(cls): # cls指向这个class本身
return cls(0, 0) # Point(0, 0)

def draw(self):
print(f"Point ({self.x}, {self.y})")


point = Point.zero() # 工厂方法:将复杂的对象构造(比如多种初始值)交给类方法去完成
point.draw() # 输出 Point (0, 0)

Magic Methods

编译器会自动调用Magic Methods。

guide book

1
2
3
4
5
6
7
8
9
10
11
12
class Point:
def __init__(self, x, y):
self.x = x
self.y = y

def draw(self):
print(f"Point ({self.x}, {self.y})")


point = Point(1, 2)
print(point)
print(str(point))

输出:

1
2
<__main__.Point object at 0x0000020E4C1A6FD0>
<__main__.Point object at 0x0000020E4C1A6FD0>

自定义magic function __str__:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Point:
def __init__(self, x, y):
self.x = x
self.y = y

def __str__(self):
return f"({self.x}, {self.y})"

def draw(self):
print(f"Point ({self.x}, {self.y})")


point = Point(1, 2)
print(point)
print(str(point))

输出:

1
2
(1, 2)
(1, 2)

Comparing Objects

1
2
3
4
5
6
7
8
9
class Point:
def __init__(self, x, y):
self.x = x
self.y = y


point = Point(1, 2)
other = Point(1, 2)
print(point == other)

输出:False。因为这样比较的是两个reference,是不同的。

方法:重定义magic functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Point:
def __init__(self, x, y):
self.x = x
self.y = y

def __eq__(self, other):
return self.x == other.x and self.y == other.y

def __gt__(self, other):
return self.x > other.x and self.y > other.y


point = Point(10, 20)
other = Point(1, 2)
print(point == other)
print(point > other)

这时就是比较两个 object 的 x 和 y 的值了

Supporting Arithmetic Operations

1
2
3
4
5
6
7
8
9
10
11
12
13
class Point:
def __init__(self, x, y):
self.x = x
self.y = y

def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)


point = Point(10, 20)
other = Point(1, 2)
combined = point + other
print(f"Point ({combined.x}, {combined.y})")

输出:

1
Point (11, 22)

Creating Custom Containers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class TagCloud:
def __init__(self):
self.tags = {}

def add(self, tag):
self.tags[tag.lower()] = self.tags.get(tag.lower(), 0) + 1

def __getitem__(self, tag):
return self.tags.get(tag.lower(), 0)

def __setitem__(self, tag, count):
self.tags[tag.lower()] = count

def __len__(self):
return len(self.tags)

def __iter__(self): # make it iterable
return iter(self.tags)


cloud = TagCloud()
cloud.add("Python")
cloud.add("python")
cloud.add("python")
print(cloud.tags)

print(cloud["python"]) # __getitem__实现
cloud["java"] = 10 # __setitem__实现
print(cloud.tags)

print(len(cloud)) # __len__实现

输出:

1
2
3
4
{'python': 3}
3
{'python': 3, 'java': 10}
2

Private Members

将光标放到 tags 那,F2,改成__tags,回车,所有地方都改成__tags了,这就是 private member

1
2
3
4
5
6
7
8
9
class TagCloud:
def __init__(self):
self.__tags = {}


cloud = TagCloud()
print(cloud.__dict__)
print(cloud._TagCloud__tags)
print(cloud.__tags)

输出:

1
2
3
4
5
6
{'_TagCloud__tags': {}}
{}
Traceback (most recent call last):
File "e:\HelloWorld\app.py", line 24, in <module>
print(cloud.__tags)
AttributeError: 'TagCloud' object has no attribute '__tags'

不能直接用名字调用,但是实际上python的 private member还是外部可见的,用__dict__就能查看到,用那个名字’_TagCloud__tags’也还能访问到。

Properties

Java 风格的 getter 和 setter,不 pythonic:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Product:
def __init__(self, price):
self.set_price(price)

def get_price(self):
return self.__price

def set_price(self, value):
if value < 0:
raise ValueError("Price cannot be negative.")
self.__price = value


product = Product(-50)

pythonic coding:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Product:
def __init__(self, price):
self.price = price

@property
def price(self):
return self.__price

@price.setter
def price(self, value):
if value < 0:
raise ValueError("Price cannot be negative.")
self.__price = value

# price = property(get_price, set_price)


product = Product(-10)
print(product.price)

如果将setter删掉,price 就是只读的。

Inheritance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Animal:
def __init__(self):
self.age = 1

def eat(self):
print("eat")


# Animal: Parent, Base
# Mammal: Child, Sub
class Mammal(Animal):
def walk(self):
print("walk")


class Fish(Animal):
def swim(self):
print("swim")


m = Mammal()
m.eat()
print(m.age)

输出:

1
2
eat
1

The Object Class

所有 class 都继承于 object 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Animal:
def __init__(self):
self.age = 1

def eat(self):
print("eat")


# Animal: Parent, Base
# Mammal: Child, Sub
class Mammal(Animal):
def walk(self):
print("walk")


m = Mammal()
print(isinstance(m, Animal)) # True
print(isinstance(m, Mammal)) # True
print(isinstance(m, object)) # True
print(issubclass(Mammal, Animal)) # True
print(issubclass(Mammal, object)) # True

Method Overriding

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Animal:
def __init__(self):
print("Animal Constructor")
self.age = 1

def eat(self):
print("eat")


# Animal: Parent, Base
# Mammal: Child, Sub
class Mammal(Animal):
def __init__(self):
super().__init__() # 调用父类的__init__(),否则会被子类的覆盖掉而不执行
print("Mammal Constructor")
self.weight = 2

def walk(self):
print("walk")


m = Mammal()
print(m.age)
print(m.weight)

输出:

1
2
3
4
Animal Constructor
Mammal Constructor
1
2

Multi-level Inheritance

Abuse inheritance:

​ Employee - Person - LivingCreature - Thing

make software complex

Multiple Inheritance

Abuse case:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Employee:
def greet(self):
print("Employee Greet")


class Person:
def greet(self):
print("Person Greet")


class Manager(Employee, Person):
pass


manager = Manager()
manager.greet()

将 class Manager(Employee, Person) 中的父类换个顺序就有完全不同的输出:

(Employee, Person) 输出: Employee Greet

(Person, Employee) 输出: Person Greet

Good Example

1
2
3
4
5
6
7
8
9
10
11
12
class Flyer:
def fly(self):
pass


class Swimmer:
def swim(self):
pass


class FlyingFish(Flyer, Swimmer):
pass

子类的两个父类应该是没有什么共同点的,而子类兼具两个父类的method。

A Good Example of Inheritance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class InvalidOperationError(Exception):
pass


class Stream:
def __init__(self):
self.opened = False

def open(self):
if self.opened:
raise InvalidOperationError("Stream is already opened.")
self.opened = True

def close(self):
if not self.opened:
raise InvalidOperationError("Stream is already closed.")
self.opened = False


class FileStream(Stream):
def read(self):
print("Reading data from a file.")


class NetworkStream(Stream):
def read(self):
print("Reading data from a network.")

Abstract Base Classes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
from abc import ABC, abstractmethod


class InvalidOperationError(Exception):
pass


class Stream(ABC): # 继承自ABC(抽象类简写)
def __init__(self):
self.opened = False

def open(self):
if self.opened:
raise InvalidOperationError("Stream is already opened.")
self.opened = True

def close(self):
if not self.opened:
raise InvalidOperationError("Stream is already closed.")
self.opened = False

@abstractmethod
def read(self): # 抽象方法
pass


class FileStream(Stream):
def read(self):
print("Reading data from a file.")


class NetworkStream(Stream):
def read(self):
print("Reading data from a network.")


class MemoryStream(Stream):
def read(self):
print("Reading data from the memory.")


stream = MemoryStream()
stream.read()

👉 Stream 不能创建实例

👉 Stream 的子类必须实现 read() ,或者也是抽象类

Polymorphism

Many Forms

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from abc import ABC, abstractmethod


class UIControl(ABC):
@abstractmethod
def draw(self):
pass


class TextBox(UIControl):
def draw(self):
print("TextBox")


class DropDownList(UIControl):
def draw(self):
print("DropDownList")


def draw(controls): # 在run-time才知道control具体是什么
for control in controls:
control.draw()


ddl = DropDownList()
textbox = TextBox()
draw([ddl, textbox]) # 多种对象实现一种方法

输出:

1
2
DropDownList
TextBox

Duck Typing

If it walks and quacks like a duck, it is a duck.

上例中并不需要抽象类UIControl。只要类中实现了draw()方法,python就认为是个UIControl,就能执行draw(controls)方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from abc import ABC, abstractmethod


class TextBox:
def draw(self):
print("TextBox")


class DropDownList:
def draw(self):
print("DropDownList")


def draw(controls):
for control in controls:
control.draw()


ddl = DropDownList()
textbox = TextBox()
draw([ddl, textbox])

Extending Built-in Types

Demo 1: 扩展str类

1
2
3
4
5
6
7
8
class Text(str):
def duplicate(self):
return self + self


text = Text("Python")
print(text.duplicate()) # 本类方法
print(text.lower()) # 父类方法

输出:

PythonPython

python

Demo 2: 扩展list类

1
2
3
4
5
6
7
8
class TrackableList(list):
def append(self, object):
print("Append called")
super().append(object) # 调用父类方法


list = TrackableList()
list.append("1")

输出:

Append called

Data Classes

对于一个只有data而没有方法的类:

1
2
3
4
5
6
7
8
9
10
11
12
class Point:
def __init__(self, x, y):
self.x = x
self.y = y

def __eq__(self, other) -> bool:
return self.x == other.x and self.y == other.y


p1 = Point(1, 2)
p2 = Point(1, 2)
print(p1 == p2)

可以用 namedtuple make code cleaner:

1
2
3
4
5
6
7
8
9
10
from collections import namedtuple

Point = namedtuple("Point", ["x", "y"])
p1 = Point(x=1, y=2) # must be keyword arguments
p2 = Point(x=1, y=2)

print(p1 == p2) # True
print(p1.x) # 像类一样调用:1

p1.x = 10 # AttributeError: can't set attribute

只能新建一个namedtuple,用p1指向:

1
p1 = Point(x=10, y=2)

Mosh的课程网址

Handling Exceptions

1
2
3
4
5
6
7
8
9
10
try:
age = int(input("Age: "))
except ValueError as ex:
print("You didn't enter a valid age.")
print(ex) # 输出错误信息
print(type(ex)) # 显示错误类型
else: # 没有exception就执行这一块
print("No exceptions were thrown.")

print("Execution continues.")

运行结果:

1
2
3
4
5
6
7
8
9
10
11
>python app.py
Age: 10
No exceptions were thrown.
Execution continues.

>python app.py
Age: a
You didn't enter a valid age.
invalid literal for int() with base 10: 'a'
<class 'ValueError'>
Execution continues.

Handling Different Exceptions

1
2
3
4
5
6
7
try:
age = int(input("Age: "))
xfactor = 10 / age
except (ValueError, ZeroDivisionError): # 多种exception之一即执行
print("You didn't enter a valid age.")
else:
print("No exceptions were thrown.")

输出结果:

1
2
3
> python app.py
Age: 0
You didn't enter a valid age.

Cleaning Up

1
2
3
4
5
6
7
8
9
10
try:
file = open("app.py")
age = int(input("Age: "))
xfactor = 10 / age
except (ValueError, ZeroDivisionError):
print("You didn't enter a valid age.")
else:
print("No exceptions were thrown.")
finally:
file.close()

无论有没有出现exception,都要释放资源,那就放在finally块里,无论有没有exception都会执行的。

The With Statement

使用with语句打开文件,随后会自动释放掉,就不用file.close()了:

1
2
with open("app.py") as file:
print("File opened.")

file有__enter__和__exit__两个magic function,最后会自动调用__exit__,所以不用手动释放。

Raising Exceptions

1
2
3
4
5
6
7
8
9
10
def calculate_xfactor(age):
if age <= 0:
raise ValueError("Age cannot be 0 or less.")
return 10 / age


try:
calculate_xfactor(-1)
except ValueError as error:
print(error)

Cost of Raising Exceptions

Mosh 不建议raise exceptions,因为代价有点大。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from timeit import timeit

code1 = """
def calculate_xfactor(age):
if age <= 0:
raise ValueError("Age cannot be 0 or less.")
return 10 / age


try:
calculate_xfactor(-1)
except ValueError as error:
pass
"""

code2 = """
def calculate_xfactor(age):
if age <= 0:
return None
return 10 / age


xfactor = calculate_xfactor(-1)
if xfactor == None:
pass
"""

print("first code=", timeit(code1, number=10000))
print("second code=", timeit(code2, number=10000))

输出结果:

1
2
first code= 0.0040202000000000016
second code= 0.0015166999999999958

可以看到raise exception的用时是普通方法的两倍多。

Mosh的课程网址

List

1
2
3
4
5
6
7
8
letters = ["a", "b", "c"]
matrix = [[0, 1], [2, 3]]

zeros = [0] * 5 # [0,0,0,0,0]
combined = zeros + letters # [0, 0, 0, 0, 0, 'a', 'b', 'c']
numbers = list(range(20))
chars = list("Hello World") # ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd']
print(len(chars)) # 11

Demo 1

1
2
3
4
5
6
7
letters = ["a", "b", "c", "d"]
print(letters[0])
print(letters[-1])
letters[0] = "A"
print(letters)
print(letters[:3])
print(letters[::2])
1
2
3
4
5
a
d
['A', 'b', 'c', 'd']
['A', 'b', 'c']
['A', 'c']

Demo 2

1
2
3
4
numbers = list(range(10))
print(numbers)
print(numbers[::2])
print(numbers[::-1]) # 倒序输出

输出结果:

1
2
3
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 2, 4, 6, 8]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

Unpacking Lists

Demo 1

1
2
3
numbers = [1, 2, 3]
first, second, third = numbers # first=1, second=2, third=3
first, second, _ = numbers

Demo 2

1
2
3
4
numbers2 = [1, 2, 3, 4, 4, 4, 4, 4, 4]
first, second, *other = numbers2
print(first) # 1
print(other) # [3, 4, 4, 4, 4, 4, 4]

与此同理:

1
2
3
4
5
6
7
8
9
def multiply(*numbers):
total = 1
for number in numbers:
total *= number
return total


print(multiply(2, 3, 4, 5))
print(multiply(4, 5, 6, 2, 5))

Another demo:

1
2
3
4
numbers2 = [1, 2, 3, 4, 4, 4, 4, 4, 9]
first, *other, last = numbers2
print(first, last) # 1 9
print(other) # [2, 3, 4, 4, 4, 4, 4]

输出:

1
2
{'id': 1, 'name': 'John', 'age': 22}
John

Looping over Lists

1
2
3
letters = ["a", "b", "c"]
for letter in enumerate(letters):
print(letter)

输出结果:

1
2
3
(0, 'a')
(1, 'b')
(2, 'c')

unpacking tuple:

1
2
3
letters = ["a", "b", "c"]
for index, letter in enumerate(letters):
print(index, letter)

输出结果:

1
2
3
0 a
1 b
2 c

Adding/Removing Items

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
letters = ["a", "b", "c", "d", "e"]

# Add
letters.append("f") # 在末尾加入
letters.insert(0, "-") # 在首部加入
print(letters)

# Remove
letters.pop() # 删除最后一个元素
print(letters)

letters.remove("b") # 删除特定元素
print(letters)

del letters[0:3] # 删除多个元素
print(letters)

letters.clear() # 清除所有元素
print(letters)

输出结果:

1
2
3
4
5
['-', 'a', 'b', 'c', 'd', 'e', 'f']
['-', 'a', 'b', 'c', 'd', 'e']
['-', 'a', 'c', 'd', 'e']
['d', 'e']
[]

Finding Items

1
2
3
4
letters = ["a", "b", "c"]
print(letters.count("d")) # 0
if "c" in letters:
print(letters.index("c")) # 2

Sorting Lists

Demo 1

1
2
3
numbers = [3, 51, 2, 8, 6]
numbers.sort(reverse=True) # 倒序
print(numbers)

输出结果:

1
[51, 8, 6, 3, 2]

Demo 2

built-in function: 不改变原数组

1
2
3
numbers = [3, 51, 2, 8, 6]
print(sorted(numbers, reverse=True))
print(numbers)

输出结果:

1
2
[51, 8, 6, 3, 2]
[3, 51, 2, 8, 6]

Demo 3

1
2
3
4
5
6
7
8
9
10
11
12
13
items = [
("product1", 10),
("product2", 9),
("product3", 12),
]


def sort_item(item):
return item[1]


items.sort(key=sort_item)
print(items)

输出结果:

1
[('product2', 9), ('product1', 10), ('product3', 12)]

Lambdas

改造上述的例子(Demo 3):

1
2
3
4
5
6
7
8
9
items = [
("product1", 10),
("product2", 9),
("product3", 12),
]


items.sort(key=lambda item: item[1])
print(items)

语法:

1
lambda parameters:expression

Map Function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
items = [
("product1", 10),
("product2", 9),
("product3", 12),
]


# prices = []
# for item in items:
# prices.append(item[1])

# print(prices)

# A better way
prices = list(map(lambda item: item[1], items))
print(prices)

输出:

1
[10, 9, 12]

Filter Function

1
2
3
4
5
6
7
8
items = [
("product1", 10),
("product2", 9),
("product3", 12),
]

filtered = list(filter(lambda item: item[1] >= 10, items))
print(filtered)

输出结果:

1
[('product1', 10), ('product3', 12)]

List Comprehension

1
[expression for item in items]

改造上面的 map function 和 filter function:

1
2
3
prices = [item[1] for item in items]

filtered = [item for item in items if item[1] >= 10]

Zip Function

1
2
3
4
list1 = [1, 2, 3]
list2 = [10, 20, 30]

print(list(zip("abc", list1, list2)))

输出结果:

1
[('a', 1, 10), ('b', 2, 20), ('c', 3, 30)]

Stacks

LIFO: Last In - First Out

1
2
3
4
5
6
browsing_session = []
browsing_session.append(1) # 压栈
browsing_session.append(2)

if not not browsing_session:
print(browsing_session.pop()) # 出栈

Queues

FIFO: First In - First Out

1
2
3
4
5
6
7
8
9
10
11
from collections import deque

queue = deque([])
queue.append(1) # 入队
queue.append(2)
queue.append(3)
queue.popleft() # 出队
print(queue)

if not queue: # 检查队列是否为空
print("Empty")

Tuples

1
2
3
4
5
# 都是tuple:
point = 1, 2
# point = 1,
# point = ()
print(type(point))

Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
point = (1, 2) + (3, 4)
print(point) # (1, 2, 3, 4)

point = (1, 2) * 3
print(point) # (1, 2, 1, 2, 1, 2)

point = tuple([1, 2])
print(point) # (1, 2)


point = tuple("hello")
print(point) # ('h', 'e', 'l', 'l', 'o')

point = (1, 2, 3)
print(point[0:2]) # (1, 2)

x, y, z = point
if 10 in point:
print("exists")

Swapping Variables

1
2
3
4
5
6
7
x = 10
y = 11

z = x
x = y
y = z
print(x, y) # 11 10

simpler way:

1
2
x, y = y, x
print(x, y)

实际上是先定义了一个tuple,然后给x, y赋值,即 x, y = (11, 10)

Arrays

1
2
3
4
5
6
7
8
from array import array

numbers = array("i", [1, 2, 3])
# numbers[0] = 1.0 # TypeError: integer argument expected, got float

list1 = [1.0, 1, 2, 3]
print(type(list1[0])) # <class 'float'>
print(type(list1[1])) # <class 'int'>

arrays 可以调用 append, pop, index 等与 lists 相同的 built-in function,但是只能有一种数据类型(由参数 typecode 如 ”i“ 指定)

而一个 list 里面可以有多种数据类型:

1
2
3
list1 = [1.0, 1, 2, 3]
list1.append("hello")
print(list1) # [1.0, 1, 2, 3, 'hello']

Sets

一个 set 内不能有重复项

1
2
3
numbers = [1, 1, 2, 3, 4]
first = set(numbers)
print(first) # {1, 2, 3, 4}
1
2
3
4
5
6
7
second = {1, 4}
second.add(5)
print(second) # {1, 4, 5}

second.remove(5)
len(second)
print(second) # {1, 4}

Demo

1
2
3
4
5
6
7
8
9
numbers = [1, 1, 2, 3, 4]
first = set(numbers)

second = {1, 5}

print(first | second) # 在first或在second
print(first & second) # 在first也在second
print(first - second) # 在first不在second
print(first ^ second) # 在first或在second但不同时在两个set

输出结果:

1
2
3
4
{1, 2, 3, 4, 5}
{1}
{2, 3, 4}
{2, 3, 4, 5}

set是无序的,不能用index,如first[0]是不合法的

1
2
if 1 in first:		# 判断set里是否有某元素
print("yes")

Dictionary

创建

1
2
3
# Two ways to create a dictionary
point = {"x": 1, "y": 2}
point = dict(x=1, y=2)

增改

1
2
3
4
point["x"] = 10		# modify element
print(point) # {'x': 10, 'y': 2}
point["z"] = 20 # create new element
print(point) # {'x': 10, 'y': 2, 'z': 20}

获取

1
2
print(point.get("a"))			# None
print(point.get("a", 0)) # 0 (若没有指定的key,则返回指定值0)

删除

1
2
del point["x"]		# 删除元素
print(point) # {'y': 2, 'z': 20}

for loop

1
2
3
4
5
6
7
8
for key in point:
print(key, point[key])

for item in point.items(): # 返回tuple
print(item)

for key, value in point.items():
print(key, value)

返回:

1
2
3
4
5
6
y 2
z 20
('y', 2)
('z', 20)
y 2
z 20

Dictionary Comprehensions

1
2
3
4
5
# values = []
# for x in range(5):
# values.append(x * 2)

values = [x * 2 for x in range(5)] # 作用同上

set、dictionary 的:

1
2
3
4
5
values = {x * 2 for x in range(5)}
print(values) # {0, 2, 4, 6, 8}

values = {x: x * 2 for x in range(5)}
print(values) # {0: 0, 1: 2, 2: 4, 3: 6, 4: 8}

Generators

用于有大量(无限)元素,要节省内存空间的情形

1
2
3
4
5
6
7
from sys import getsizeof

values = (x * 2 for x in range(100000)) # generator
print("gen:", getsizeof(values))

values = [x * 2 for x in range(100000)] # list
print("list:", getsizeof(values))

输出:

1
2
gen: 112
list: 800984

对于 generator object, 无论有多少元素,都只占112的位置(就算只有一个元素也是)。generator object 在迭代时才生成元素,而不是将所有的元素都存在内存中。

Unpacking Operator

Demo 1

1
2
3
numbers = [1, 2, 3]
print(numbers) # [1, 2, 3]
print(*numbers) # 1 2 3

numbers 是一个list, 而 *numbers 是 unpack 之后的3个独立的数

Demo 2

1
2
3
values = list(range(5))
values = [*range(5), *"Hello"]
print(values) # [0, 1, 2, 3, 4, 'H', 'e', 'l', 'l', 'o']

Demo 3

组合两个lists:

1
2
3
4
first = [1, 2]
second = [3]
values = [*first, "a", *second, *"Hello"]
print(values) # [1, 2, 'a', 3, 'H', 'e', 'l', 'l', 'o']

Demo 4

组合两个字典:

1
2
3
4
first = {"x": 1}
second = {"x": 10, "y": 2}
combined = {**first, **second, "z": 1}
print(combined) # {'x': 10, 'y': 2, 'z': 1}

相同 key 值的元素 (“x”) 取最后一个元素的 value (10)

Exercise

得出一个句子中出现频率最高的字母:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pprint import pprint

sentence = "This is a common interview question"

char_frequency = {}
for char in sentence:
if char in char_frequency:
char_frequency[char] += 1
else:
char_frequency[char] = 1
pprint(char_frequency, width=1) # 一行只打印一个

char_frequency_sorted = sorted(
char_frequency.items(),
key=lambda kv: kv[1],
reverse=True)

print(char_frequency_sorted[0])

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{' ': 5,
'T': 1,
'a': 1,
'c': 1,
'e': 3,
'h': 1,
'i': 5,
'm': 2,
'n': 3,
'o': 3,
'q': 1,
'r': 1,
's': 3,
't': 2,
'u': 1,
'v': 1,
'w': 1}
('i', 5)

我的改进:

1
2
3
4
5
6
7
8
9
10
11
12
#char_frequency = {}
#for char in sentence:
# if char in char_frequency:
# char_frequency[char] += 1
# else:
# char_frequency[char] = 1
# pprint(char_frequency, width=1) # 一行只打印一个

char_frequency = {}
for char in sentence:
char_frequency[char] = char_frequency.get(char, 0) + 1
pprint(char_frequency, width=1)

Mosh的课程网址

Arguments

1
2
3
4
5
6
7
def greet(first_name, last_name):
print(f"Hi {first_name} {last_name}")
print("Welcome aboard")


greet("Stone", "Purple")
greet("Stephen", "Curry")

Types of Functions

1
2
👉 1 - Perform a task
👉 2 - Return a value
1
2
3
4
5
6
7
8
9
10
11
12
13
def greet(name):
""" Type 1 function """
print(f"Hi {name}")


def get_greeting(name):
""" Type 2 function """
return f"Hi {name}"


message = get_greeting("Stone")
file = open("content.txt", "w")
file.write(message)

python return None by default.

1
print(greet("Stone"))

输出结果:

1
None

Types of Arguments

Keyword Arguments

1
2
3
4
5
6
def increment(number, by):
return number + by


# more readable: by=...
print(increment(2, by=1))

Default Arguments

1
2
3
4
5
def increment(number, by=1):
return number + by


print(increment(2))

所有 optional arguments 应该在无 default value 的 argument 后面

*args

输入参数数量可变(存成一个tuple,且iterable)

1
2
3
4
5
6
7
8
9
def multiply(*numbers):
total = 1
for number in numbers:
total *= number
return total


print(multiply(2, 3, 4, 5))
print(multiply(4, 5, 6, 2, 5))

输出

1
2
120
1200

**args

打包 keyword arguments 成 dict 给函数体调用

1
2
3
4
5
6
def save_user(**user):
print(user)
print(user["name"])


save_user(id=1, name="John", age=22)

输出:

1
2
{'id': 1, 'name': 'John', 'age': 22}
John

Scope

局部变量局部可见

全局变量全局可见

1
2
3
4
5
6
7
8
9
message = "a"


def greet(name):
message = "b"


greet("Stone")
print(message)

输出结果:

1
a

因为 Python 将 greet 函数内的 message 视为 local variable,尽管它与全局变量同名。

Debugging

1
2
3
4
5
👉 F9 			-- 设置断点
👉 F5 -- 开始调试
👉 F10 -- 下一步
👉 F11 -- 进入 function 内单步 debug
👉 Shift+F11 -- 退出 function 内单步 debug

VSCode Tricks (Windows)

1
2
3
4
5
6
7
8
👉 Home 		光标移到行首
👉 End 光标移到行尾
👉 Ctrl+Home 光标移到文件头
👉 Ctrl+End 光标移到文件尾
👉 Ctrl+L 选择当前行
👉 Alt+↑or↓ 代码上移或下移
👉 Shift+Alt+↓ 选中某(几)行后用此组合键复制粘贴
👉 Ctrl+/ 多行注释

Mosh的课程网址

Comparison Operators

1
2
3
4
5
6
7
8
9
10
11
12
>>> 10 > 3
True
>>> 10 >= 3
True
>>> 10 < 20
True
>>> 10 == 10
True
>>> 10 == "10"
False
>>> 10 != "10"
True
1
2
3
4
5
6
7
8
>>> "bag" > "apple" 
True
>>> "bag" == "BAG"
False
>>> ord("b")
98
>>> ord("B")
66

Chaining Comparison Operators

1
2
3
4
# age should be between 18 and 65
age = 22
if 18 <= age < 65:
print("Eligible")

Conditional Statements

1
2
3
4
5
6
7
8
9
10
11
12
temperature = 15
if temperature > 30:
print("It is warm")
print("Drink water")

elif temperature > 20:
print("It's nice")

else:
print("It's cold")

print("Done")

Ternary Operator

1
2
3
4
5
6
7
8
9
10
age = 2

# if age >= 18:
# message = "Eligible"
# else:
# message = "Not eligible"

message = "Eligible" if age >= 18 else "Not eligible"

print(message)

Logical Operators

1
2
3
4
5
6
7
8
9
10
# logical operators: and, or, not

high_income = True
good_credit = False
student = False

if (high_income or good_credit) and not student:
print("Eligible")
else:
print("Not eligible")

Short-circuit Evaluation

多条件判断时:

and 前面的 expression 为 False, 则不再(执行)判断后面的expression

or 前面的 expression 为 True, 则不再(执行)判断后面的expression

For Loops

1
2
for number in range(1, 10, 2):		# step: 2
print("Attempt", number, number * ".")

输出:

1
2
3
4
5
Attempt 1 .
Attempt 3 ...
Attempt 5 .....
Attempt 7 .......
Attempt 9 .........

For…Else

1
2
3
4
5
6
7
8
successful = False
for number in range(3):
print("Attempt")
if successful:
print("Successful")
break
else:
print("Attempted 3 times and failed.")

如果一直没有执行break,就执行下面的else

输出结果:

1
2
3
4
Attempt
Attempt
Attempt
Attempted 3 times and failed

Nested Loops

1
2
3
for x in range(5):
for y in range(3):
print(f"({x}, {y})")

输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(0, 0)
(0, 1)
(0, 2)
(1, 0)
(1, 1)
(1, 2)
(2, 0)
(2, 1)
(2, 2)
(3, 0)
(3, 1)
(3, 2)
(4, 0)
(4, 1)
(4, 2)

Iterables

1
2
3
4
5
6
# Iterable : range(), string, list...
for x in "Python":
print(x)

for x in [1, 2, 3, 4]:
print(x)

输出:

1
2
3
4
5
6
7
8
9
10
P
y
t
h
o
n
1
2
3
4

While Loops

1
2
3
4
command = ""
while command.lower() != "quit":
command = input(">")
print("ECHO", command)

输出结果:

1
2
3
4
5
6
>2+2
ECHO 2+2
>3*4
ECHO 3*4
>quit
ECHO quit

Infinite Loops

1
2
3
4
5
while True:
command = input(">")
print("ECHO", command)
if command.lower() == "quit":
break

Exercise

编写一段code,输出1~10之间(不含10)的偶数,如下:

1
2
3
4
5
2
4
6
8
We have 4 even numbers

Mosh的课程网址

Primitive Types

1
2
3
4
students_count = 1000
rating = 4.99
is_published = False # case sensitive (区分大小写)
course_name = "Python Programming"

String

1
2
3
4
5
6
course_name = "Python Programming"
message = """
Hi John
This is Stone from China
blablabla
"""
1
2
3
4
5
6
7
8
course_name = "Python Programming"
print(len(course_name)) # 18
print(course_name[0]) # P
print(course_name[-1]) # g
print(course_name[0:3]) # Pyt
print(course_name[0:]) # Python Programming
print(course_name[:3]) # Pyt
print(course_name[:]) # Python Programming

Escape Sequences

转义字符

1
2
3
course_name = "Python \"Programming"
print(course_name) # Python "Programming
# \" \' \\ \n

Formatted Strings

1
2
3
4
5
first = "Stone"
last = "Purple"
full = f"{first} {last}"
print(full) # Stone Purple
print(f"{len(first)} {2 + 2}") # 5 4

String Methods

1
2
3
4
5
6
7
8
9
course = "  Python Programming"
print(course.upper())
print(course.lower())
print(course.title())
print(course.strip()) # lstrip, rstrip
print(course.find("Pro"))
print(course.replace("P", "J"))
print("Pro" in course)
print("swift" not in course)

输出结果:

1
2
3
4
5
6
7
8
  PYTHON PROGRAMMING
python programming
Python Programming
Python Programming
9
Jython Jrogramming
True
True

Numbers

1
2
3
4
5
6
7
8
9
10
11
12
x = 1
x = 1.1
x = 1 + 2j # complex number

# + - * / % // **
print(10 / 3) # 3.3333333333333335
print(10 // 3) # 3
print(10 ** 3) # 1000

x = 10
x = x + 3
x += 3

Working with Numbers

1
2
3
4
5
6
import math

print(round(2.9)) # 3
print(abs(-2.9)) # 2.9

print(math.ceil(2.2)) # 3

Type Conversion

1
2
3
4
x = input("x: ")
print(type(x))
y = int(x) + 1 # type conversion
print(f"x: {x}, y: {y}")
1
2
3
x: 2
<class 'str'>
x: 2, y: 3

对于bool(x),为 False 的情况只有:“”,0,None

1
print(bool(""))			# False

Quiz

👉 What are the primitive types in Python?

👉 What are the results?

1
2
3
fruit = "Apple"
print(fruit[1])
print(fruit[1:-1])

👉 What are the results?

1
2
print(10 % 3)
print(bool("False"))