Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
变量和简单的数据类型
字符串
在字符串使用变量(f字符串)
例如
firts="ada"
last="lace"
full=f"{firts}{last}"
print(f"Hello,{full.title()}!")
制表符是t
删除空白(_strip)
用于删除字符串空白,当然了,这个删除不是对于变量本身的,所以要修改变量的值必须使用重新赋值的方法
lstrip() | rstrip() | strip() |
---|---|---|
删除左端空白 | 删除右端空白 | 删除两侧空白 |
删除前缀(removeprefix())
指定前缀进行删除,比如说a.removeprefix('https://')
处理单双引号问题
由于字符串可以用单引号也可以用双引号,所以会出现在单引号的句子里面又出现了一个单引号导致无法运行
数
乘方运算是**
,比如3**2
输出就是9
将任意的两个数(即使是整数)相除得到的结果也总是浮点数,而在其他的运算中,如果一个操作数是整数,另外一个是浮点数,那么结果也是浮点数.
总而言之操作数只要有浮点数那默认就会得到浮点数
数中的下划线
表示数字使用的下划线不会被储存
a=14_000_000_000
最后打印出来还是14000000000,下划线不会被储存进去
多变量赋值
逗号把变量名和付的值分开a,b,c=0,1,2
常量
通常用全大写字母把一个变量视为一个常量
注释
后面跟着的就是注释
列表
用方括号[]表示列表,用逗号分隔其中的元素
bicycles=['Giant','Trek','Cannondale','Specialized'];
访问列表元素
跟数组一样的
更改列表元素
更改内容的话重新赋值就行了
在列表末尾添加元素(append): a.append(' ')
在列表中插入元素(insert):a.insert(0,' ')
从列表中删除元素:
- del 删除任意位置的元素
del a[0]
删除之后就无法再访问了 - pop 将元素从列表中删除之后继续使用它的值,pop()可以删除列表之中的元素
比如我们可以将一个变量赋值为我们需要弹出的东西
pop()括号没东西就是删除末尾的,有的话就是删除指定位置的元素
poped=a.pop()
- remove
a.remove('element')
使用remove()删除了元素之后也可以继续使用他的值
管理列表
sort()
永久的修改列表元素的排列顺序.
例如 a.sort()会对a按照字母顺序进行排序,当然我们也可以用a.sort(reverse=True)来进行与字母顺序相反的排序
sorted()
保留列表元素原本的排列顺序但是以特定的顺序来呈现他们,要反向排序的方式和sort()一样
反向打印列表
反转排列顺序可以使用reverse()的方式
不是按照与字母顺序相反的顺序排列只是反转列表元素的排列顺序
确定列表的长度
len(a)
从1开始的
列表的索引
最后一个列表元素等同于[-1] (只有列表为空的时候这种访问方式才会导致错误)
操作列表
遍历整个列表
循环
for cat in cats:
上面的代码中,cat是变量,cats是列表,会从cats[0]开始遍历,每次遍历里面都会把cats[i]赋值给cat. 注意冒号
创建数值列表
range()
生成在指定范围内的数
注意是前闭后开的
如果只有一个参数那就会从0开始,比如range(6)返回[0,5].
list()
可以用list()把range()的结果直接转化为列表,比如a=list(range(1,6))
使用range的时候可以指定步长,比如a=list(range(2,11,2))
简单统计
min,max,sum
列表推导式
比如我们可以这样创建一个1到10的二次方列表
s=[value**2 for value in range(1,11)]
使用列表的一部分
切片(slice)
切片
同样是[ )样式的
比如
players=['a','b','c','d']
print(players[0:3])
会输出a b c
没有第一个索引的话会自动从列表开头开始,没有后一个同理
不过如果想要输出最后三个也可以使用players[-3:]
,顺序打印的,比如说打印b c d
遍历切片
比如这样
for player in players[:3]:
print(player.title)
复制列表
创建一个包含整个列表的切片(同时省略起始索引还有终止索引([:])即可)
比如
a=['a','b','c','d']
b=a[:]
元组
不可变的列表即为元组
定义元组
用圆括号,比如d=(200,50)
严格来说元组是由逗号标识的,圆括号只是让他看着更加整洁.定义一个只包含一个元素的元组,必须在元素后面加上逗号:
m=(3,)
遍历元组中的所有值
for循环
d=(200,50)
for di in d:
print(d)
修改元组变量
重新定义即可
设置代码的格式
缩进
每一级缩进四个空格
行长
每行不超过80字符,注释行长不超过72个字符
if语句
条件测试
大小写问题
在检查是否相等的1时候是区分大小写的
如果不想考虑大小写,可以用lower或者upper转换了再去比较
不等检查
! = 返回真值
数值比较
小于,小于等于,大于,大于等于
检查多个条件
- and 检查两个条件都为true
- or 一个为true整个都为true
检查特定的值是否在列表中
使用 in 这个关键字来检查列表中是否包含所指定的元素
检查特定的值是否不在列表中
关键字是 not in
布尔表达式
...
if语句
if
if else
if elif else elif可以重复用
当然了else也可以不用
确定列表非空
在if语句中将列表名用作条件表达式的时候,python将在列表至少包含一个元素的时候1返回true,为空返回flase
设置if语句的格式
建议在==.>=.<=
等比较运算符两边都添加一个空格
字典
使用字典
是一系列键值对,每一个键都和一个值相关联,可以通过键来访问与之相关联的值
用放在花括号中的键值对表示,例如
alien_0 = {'color': 'green', 'points': 5}
添加键值对
依次指定字典名,用方括号括起来的键和与该键关联的值
例如:在字典 alien_0 中添加两项信息:外星人的x坐标和y坐标
alien_0 = {'color': 'green', 'points': 5}
print(alien_0)
alien_0['x_position'] = 0
alien_0['y_position'] = 25
print(alien_0)
创建空字典
用一个空的花括号就行
alien_0 = {}
alien_0['color'] = 'green'
alien_0['points'] = 5
print(alien_0)
修改字典中的值
指定字典名,用方括号括起来的键和与该件关联的新值,比如:
alien_0['color'] = 'yellow'
删除键值对
使用del删除(需要指定字典名和要删除的键),例如
del alien_0['points']
使用get()来访问值
get()方法的第一个参数用于指定键(不可以删除),第二个参数为当指定的键不存在时要返回的值
alien_0 = {'color': 'green', 'speed': 'slow'}
point_value = alien_0.get('points', 'No point value assigned.')
如果键可能不存在建议使用get而不是方括号表示
遍历字典
遍历所有的键值对
user_0 = {
'username': 'efermi',
'first': 'enrico',
'last': 'fermi',
}
for key, value in user_0.items():
print(f"nKey: {key}")
print(f"Value: {value}")
items()返回一个键值对列表.for循环依次将每个键值对赋给特定的两个变量.
遍历字典中所有的键
在不需要使用字典中的值的时候,keys()可以使用
比如
for name in favorite_languages.keys():
print(name.title())
上述就是让python提取字典中的所有键并且依次将他们付给变量name,输出每一个人的名字
在遍历字典时,会默认遍历所有的键,也就是说keys()可以不写,当然写了更好
按特定的顺序遍历字典中的所有键
一般情况下的遍历是按照插入元素的顺序返回其中的元素
如果需要特定的顺序,可以使用:
for循环对于返回的键排序 用sorted()函数获得特定顺序排列的键列表的副本
for name in sorted(favorite_languages.keys()):
遍历字典中所有的值
values()会返回一个值列表,不包含任何键(允许重复)
如果不希望值重复,使用集合(set),其中的元素都是独一无二的
for language in set(favorite_languages.values()):
ps:
可以使用一对花括号创建集合然后在里面用逗号分隔
比如 languages = {'python', 'rust', 'python', 'c'}
用户输入和while循环
input()
比如
message = input("Tell me something, and I will repeat it back to you: ")
print(message)
input()接受一个参数,是向用户显示的提示
每当使用 input() 函数时,都应指定清晰易懂的提示,准确地指出 希望用户提供什么样的信息
使用int()来获取数值输入
int()的作用是将输入的字符串转换为数值,确保能够成功地执行比较操作
求模运算符(%)
一样的
while循环
例子
current_number = 1
while current_number <= 5:
print(current_number)
current_number += 1
break退出循环
直接跳出,和cpp一样
continue忽略剩下代码
和cpp一样
使用while循环处理列表和字典
删除为特定值的所有列表元素
通过while就可以删除重复的特定元素
pets = ['dog', 'cat', 'dog', 'goldfish', 'cat', 'rabbit', 'cat']
while 'cat' in pets:
pets.remove('cat')
print(pets)
使用用户输入填充字典
可以使用 while 循环提示用户输入任意多的信息
函数
定义函数
比如这个
def greet_user():
"""显示简单的问候语"""
print("Hello!")
greet_user()
def是标识
向函数传递信息
在括号里面动就行了
传递实参
函数定义中可能包含多个形参,因此函数调用中也可能包含多个实 参。向函数传递实参的方式很多:既可以使用 位置实参 ,这要求实参 的顺序与形参的顺序相同;也可以使用 关键字实参 ,其中每个实参都 由变量名和值组成;还可以使用列表和字典。
位置实参
在调用函数时,Python必须将函数调用中的每个实参关联到函数定义 中的一个形参。最简单的方式是基于实参的顺序进行关联。以这种方式关联的实参称为位置实参。
比如这种
❶ def describe_pet(animal_type, pet_name):
"""显示宠物的信息"""
print(f"nI have a {animal_type}.")
print(f"My {animal_type}'s name is {pet_name.title()}.")
❷ describe_pet('hamster', 'harry')
关键字实参
关键字实参是传递给函数的名值对。这样会直接在实参中将名称和值 关联起来,因此向函数传递实参时就不会混淆了
比如
def describe_pet(animal_type, pet_name):
"""显示宠物的信息"""
print(f"nI have a {animal_type}.")
print(f"My {animal_type}'s name is {pet_name.title()}.")
describe_pet(animal_type='hamster', pet_name='harry')
默认值
编写的时候可以给每个形参指定默认值.如果在调用函数中给形参提供了实参,Python 将使用指定的实参值;否则,将使用形参的默认值。
返回值
在函数中,可以使用 return 语句将值返回到调用函数的那行代码。
让实参变成可选的
有时候,需要让实参变成可选的,以便使用函数的人只在必要时才提 供额外的信息。可以使用默认值来让实参变成可选的。
通指定默认值的方法
def get_formatted_name(first_name, last_name, middle_name=''):
"""返回标准格式的姓名"""
if middle_name:
full_name = f"{first_name} {middle_name} {last_name}"
else:
full_name = f"{first_name} {last_name}"
return full_name.title()
musician = get_formatted_name('jimi', 'hendrix')
print(musician)
musician = get_formatted_name('john', 'hooker', 'lee')
print(musician)
传递列表
在函数中修改列表
将列表传递给函数就可以对其进行修改了。在函数中对这个列表所做的任何修改都是永久的
禁止函数修改列表
可向函数传递列表的副本而不是原始列表
使用切片表示法function_name(list_name[:])
传递任意数量的实参
比如这样
def make_pizza(*toppings):
"""打印顾客点的所有配料"""
print(toppings)
make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')
形参名 *toppings 中的星号让 Python 创建一个名为 toppings 的元组,该元组包含函数收到的所有值
Python 会将实参封装到一个元组中,即便函数只收 到一个值也是如此
使用任意数量的关键字实参
需要接受任意数量的实参,但预先不知道传递给函数的会是什么样的信息.
可将函数编写成能够接受任意数量 的键值对——调用语句提供了多少就接受多少
例如
def build_profile(first, last, **user_info):
"""创建一个字典,其中包含我们知道的有关用户的一切"""
user_info['first_name'] = first
user_info['last_name'] = last
return user_info
user_profile = build_profile('albert', 'einstein',
location='princeton',
field='physics')
print(user_profile)
形参 **user_info 中的两个星号让 Python 创建一个名为 user_info 的字典,该字典包含函数收到的 其他所有名值对
将函数储存在模块中
将函数存储 在称为 模块 的独立文件中,再将模块导入(import)主程序。 import 语句可让你在当前运行的程序文件中使用模块中的代码。
导入整个模块
模块 是扩展名为.py 的文件,包含要导入程序的代码
import+模块名
比如
import pizza
pizza.make_pizza(16, 'pepperoni')
pizza.make_pizza(12, 'mushrooms', 'green peppers', 'extra
cheese')
要调用被导入模块中的函数,可指定被导入模块的名称 pizza 和函 数名 make_pizza(),并用句点隔开
如果使用这种 import 语句导入了名为 module_name.py 的整个模块,就可使用下 面的语法来使用其中的任意一个函数:
module_name.function_name()
导入特定的函数
from module_name import function_name
用逗号分隔函数名,可根据需要从模块中导入任意数量的函数:
from module_name import function_0, function_1, function_2
使用as给函数指定别名
如果要导入的函数的名称太长或者可能与程序中既有的名称冲突,可 指定简短而独一无二的 别名 (alias)
比如make_pizza() 函数指定了别名 mp()
from pizza import make_pizza as mp
通用语法
from module_name import function_name as fn
对于模块也适用
import module_name as mn
导入模块中的所有函数
使用星号(*)运算符可让 Python 导入模块中的所有函数:
由于导入了每个函数,可通过名称来调用每个 函数,无须使用 点号 (dot notation)
函数编写指南
- 应给函数指定描述性名称,且只 使用小写字母和下划线。在给模块命名时也应遵循上述约定。
- 每个函数都应包含简要阐述其功能的注释。该注释应紧跟在函数定义 后面,并采用文档字符串的格式。
- 在给形参指定默认值时,等号两边不要有空格,函数调用中的关键字实参也应遵循这种约定
类
创建和使用类
在 Python 中,首字母大写的名称指的是类
class Dog:
"""一次模拟小狗的简单尝试"""
def __init__(self, name, age):
"""初始化属性 name 和 age"""
self.name = name
self.age = age
def sit(self):
"""模拟小狗收到命令时坐下"""
print(f"{self.name} is now sitting.")
def roll_over(self):
"""模拟小狗收到命令时打滚"""
print(f"{self.name} rolled over!")
__init()__
方法
类中的函数称为方法 记得左右都是两个下划线
__init__
()是一个特殊方法,每当你根据 Dog 类创建新实例时, Python 都会自动运行它。在这个方法的名称中,开头和末尾各有两个 下划线,这是一种约定,旨在避免 Python 默认方法与普通方法发生名称冲突
以 self 为前缀的变量可供类中的所有方法使用,可以通过类的任意 实例来访问
根据类创建实例
可以将类视为有关如何创建实例的说明。
class Dog: --snip--
my_dog = Dog('Willie', 6)
print(f"My dog's name is {my_dog.name}.")
print(f"My dog is {my_dog.age} years old.")
访问属性
可以使用点号来访问属性(比如第三行)
调用方法
根据类创建了实例之后,就可以使用点号来调类中定义的任何方法了
class Dog: --snip--
my_dog = Dog('Willie', 6)
my_dog.sit()
my_dog.roll_over()
要调用方法需要指定实例名和想调用的方法
使用类和实例
给属性指定默认值
在__init()__
里面指定就行了
修改属性的值
可以用三种不同的方式修改属性的值:直接通过实例修改,通过方法 设置,以及通过方法递增(增加特定的值)
继承
如果要编写的类是一个既有的类的特殊版本,可以使用继承.当一个类继承另一个类 时,将自动获得后者的所有属性和方法。原有的类称为 父类 (parent class),而新类称为 子类 (child class)。子类不仅继承了父类的 所有属性和方法,还可定义自己的属性和方法。
子类的__init()__
方法
调用父类的__init()__
方法
class Car:
"""一次模拟汽车的简单尝试"""
def __init__(self, make, model, year):
"""初始化描述汽车的属性"""
self.make = make
self.model = model
self.year = year
self.odometer_reading = 0
def get_descriptive_name(self):
"""返回格式规范的描述性名称"""
long_name = f"{self.year} {self.make} {self.model}"
return long_name.title()
def read_odometer(self):
"""打印一个句子,指出汽车的行驶里程"""
print(f"This car has {self.odometer_reading} miles on
it.")
def update_odometer(self, mileage):
"""将里程表读数设置为给定的值"""
if mileage >= self.odometer_reading:
self.odometer_reading = mileage
else:
print("You can't roll back an odometer!")
def increment_odometer(self, miles):
"""让里程表读数增加给定的量"""
self.odometer_reading += miles
class ElectricCar(Car):
"""
电动汽车的独特之处
"""
def __init__(self, make, model, year):
"""
初始化父类的属性
"""
super().__init__(make, model, year)
my_leaf = ElectricCar('nissan', 'leaf', 2024)
print(my_leaf.get_descriptive_name())
创建子类的时候父类必须包含在当前文件中而且在子类前面,定义子类必须指定父类名称
super()使得可以调用父类的方法,使得python调用父类的__init()__
方法
给子类定义属性和方法
Class Car: --snip-- :
class ElectricCar(Car):
"""电动汽车的独特之处"""
def __init__(self, make, model, year):
"""
先初始化父类的属性,再初始化电动汽车特有的属性
"""
super().__init__(make, model, year)
self.battery_size = 40
def describe_battery(self):
"""
打印一条描述电池容量的消息
"""
print(f"This car has a {self.battery_size}-kWh battery.")
my_leaf = ElectricCar('nissan', 'leaf', 2024)
print(my_leaf.get_descriptive_name())
my_leaf.describe_battery()
重写父类中的方法
在子类中定义一个与要重写的父类方法同名的方法,这样父类方法会被忽略,只关注子类定义方法
比如我们可以重新def class(self):
将实例用作属性
有时候可能需要把类的部分搞出来,作为一个独立的类(大拆小),这种方法称为组合
class Car:
--snip--
class Battery:
"""
一次模拟电动汽车电池的简单尝试
"""
def __init__(self, battery_size=40):
"""
初始化电池的属性
"""
self.battery_size = battery_size
def describe_battery(self):
"""
打印一条描述电池容量的消息
"""
print(f"This car has a {self.battery_size}-kWh
battery.")
class ElectricCar(Car):
"""电动汽车的独特之处"""
def __init__(self, make, model, year):
"""
先初始化父类的属性,再初始化电动汽车特有的属性
"""
super().__init__(make, model, year)
self.battery = Battery()
my_leaf = ElectricCar('nissan', 'leaf', 2024)
print(my_leaf.get_descriptive_name())
my_leaf.battery.describe_battery()
导入类
类可以储存在模块中,在主程序中导入所需的模块
就是用from 文件 import 类
在一个模块中储存多个类
在一个模块中可以根据需要存储任意数量的类
从一个模块导入多个类
逗号分隔即可
from car import Car, ElectricCar
使用别名
在导入类的时候可以指定别名
比如from electric_car import ElectricCar as EC
别名就是EC了
类的编程风格
类名应当采用驼峰命名法,类名中的每个单词的首字母都大写
对于每一个类,都应当在类1定义后面紧跟一个文档字符串,用"""开头和结尾
当需要同时导入标准库中的模块和你编写的模块时,先编写导入标准 库模块的 import 语句,再添加一个空行,然后编写导入你自己编 写的模块的 import 语句
文件和异常
读取文件
读取文件的全部内容
例如读取同路径的pi_digits.txt文件
from pathlib import Path
path = Path('pi_digits.txt')
contents = path.read_text()
print(contents)
路径 (path)指的 是文件或文件夹在系统中的准确位置。Python 提供了 pathlib 模 块,让你能够更轻松地在各种操作系统中处理文件和目录。提供特定 功能的模块通常称为 库 (library)。
从 pathlib 模块导入 Path 类。Path 对象指向一个文件
read_text() 在到达文件末尾时会返回 一个空字符串,而这个空字符串会被显示为一个空行
要删除这个多出来的空行,可对字符串变量 contents 调用 rstrip()
contents = path.read_text().rstrip()
相对文件路径和绝对文件路径
相对文件路径 让 Python 到相对于当前运行的程序所在目录的指定位置去查找
可以将文件在计算机中的准确位置告诉 Python,这样就不用管 当前运行的程序存储在什么地方了
相对路径有的时候(很多时候)都会抽风(爱来自24年招新)
记得是/不是
访问文件中的各行
使用 splitlines() 方法将冗长的字符串转换为一系列行, 再使用 for 循环以每次一行的方式检查文件中的各行:
from pathlib import Path
path = Path('pi_digits.txt')
contents = path.read_text()
lines = contents.splitlines()
for line in lines:
print(line)
splitlines() 方法 返回一个列表,其中包含文件中所有的行,而我们将这个列表赋给了 变量 lines
在读取文本文件时,Python 将其中的所有文本都解释为 字符串。如果读取的是数,并且要将其作为数值使用,就必须使 用 int() 函数将其转换为整数,或者使用 float() 函数将其 转换为浮点数。
写入文件
写入一行
定义一个文件的路径后,就可使用 write_text() 将数据写入该文件了
from pathlib import Path
path = Path('programming.txt')
path.write_text("I love programming.")
注意这种方法是将原本文件内容全部删除然后重写
Python 只能将字符串写入文本文件。如果要将数值数据 存储到文本文件中,必须先使用函数 str() 将其转换为字符串格式
写入多行
write_text() 方法会在幕后完成几项工作。首先,如果 path 变 量对应的路径指向的文件不存在,就创建它。其次,将字符串写入文 件后,它会确保文件得以妥善地关闭。如果没有妥善地关闭文件,可 能会导致数据丢失或受损。
from pathlib import Path
contents = "I love programming.n"
contents += "I love creating new games.n"
contents += "I also love working with data.n"
path = Path('programming.txt')
path.write_text(contents)
说白了写入多行就是给你每一行多加一个换行符而已
在对 path 对象调用 write_text() 方法时,务必谨 慎。如果指定的文件已存在, write_text() 将删除其内容, 并将指定的内容写入其中
异常
Python 使用称为 异常 (exception)的特殊对象来管理程序执行期间 发生的错误。每当发生让 Python 不知所措的错误时,它都会创建一 个异常对象。如果你编写了处理该异常的代码,程序将继续运行;如 果你未对异常进行处理,程序将停止
异常是使用 try-except 代码块处理的。try-except 代码块让 Python 执行指定的操作,同时告诉 Python 在发生异常时应该怎么 办。在使用 try-except 代码块时,即便出现异常,程序也将继续运行
使用try-except
当你认为可能发生错误时,可编写一个 try-except 代码块来处理 可能引发的异常
比如对于print(5/0)
肯定跑不起来
try:
print(5/0)
except ZeroDivisionError:
print("You can't divide by zero!")
如果 try-except 代码块后面还有其他代码,程序将继续运行,因 为 Python 已经知道了如何处理错误
else代码块
通过将可能引发错误的代码放在 try-except 代码块中,可提高程序抵御错误的能力
关于else 代码块:只有 try代码块成功执行才需要继续执行的代码,都应放 到 else 代码块中
try:
answer = int(first_number) / int(second_number)
except ZeroDivisionError:
print("You can't divide by 0!")
else:
print(answer)
处理FileNotFoundError异常
假设有一个不存在的文件alice.txt
from pathlib import Path
path = Path('alice.txt')
contents = path.read_text(encoding='utf-8')
使用try-expect
from pathlib import Path
path = Path('alice.txt')
try:
contents = path.read_text(encoding='utf-8')
except FileNotFoundError:
print(f"Sorry, the file {path} does not exist.")
分析文本
split()默认以空白为分隔符将字符串分拆成多个部分
比如
"""计算文件大致包含多少个单词"""
words = contents.split()
num_words = len(words)
print(f"The file {path} has about {num_words} words.")
使用多个文件
通过函数来解决问题
静默失败
pass 语句,可在代码块中使用它来让 Python 什么都不做
def count_words(path):
"""计算一个文件大致包含多少个单词"""
try: --snip--
except FileNotFoundError:
pass
else: --snip-
这样即使找不到文件也不会有任何的输出
pass 语句还充当了占位符,提醒你在程序的某个地方什么都没有 做,而且以后也许要在这里做些什么。
储存数据
模块 json 让你能够将简单的 Python 数据结构转换为 JSON 格式的 字符串,并在程序再次运行时从文件中加载数据
使用json.dumps()和json.loads()
前者是储存后者是读取
from pathlib import Path
import json
numbers = [2, 3, 5, 7, 11, 13]
path = Path('numbers.json')
contents = json.dumps(numbers)
path.write_text(contents)
from pathlib import Path
import json
path = Path('numbers.json')
contents = path.read_text()
numbers = json.loads(contents)
print(numbers)
保存和读取用户生成的数据
path = Path('username.json')
contents = json.dumps(username)
path.write_text(contents)
from pathlib import Path
import json
path = Path('username.json')
if path.exists():
contents = path.read_text()
username = json.loads(contents)
print(f"Welcome back, {username}!")
else:
username = input("What is your name? ")
contents = json.dumps(username)
path.write_text(contents)
print(f"We'll remember you when you come back, {username}!")
如果指定的文件或文件夹存在, exists() 方法返回 True,否则返回 False。
测试代码
pytest的使用
更新系统中的安装的包python -m pip install --upgrade package_name
只为当前用户安装一个包python -m pip install --user package_name
测试函数
单元测试和测试用例
最简单的测试是 单元测试 (unit test),用于核实函数的某个方面没有问题
测试用例 (test case)是一组单元测试,这些单元测试一道核实函数在各种情况下的 行为都符合要求
全覆盖 (full coverage)测试 用例包含一整套单元测试,涵盖了各种可能的函数使用方式
可通过的测试
测试文件的名字必须以test_打头
,测试函数也是,如果没有以test_打头就不会被运行,但是可以被其他测试函数调用
运行测试
通过pytest运行
直接pytest即可
name_function.py
def get_formatted_name(first, last):
"""生成格式规范的姓名"""
full_name = f"{first} {last}"
return full_name.title()
names.py
from name_function import get_formatted_name
print("Enter 'q' at any time to quit.")
while True:
first = input("nPlease give me a first name: ")
if first == 'q':
break
last = input("Please give me a last name: ")
if last == 'q':
break
formatted_name = get_formatted_name(first, last)
print(f"tNeatly formatted name: {formatted_name}.")
test_name_function
from name_function import get_formatted_name
def test_first_last_name():
"""能够正确地处理像 Janis Joplin 这样的姓名吗?"""
formatted_name = get_formatted_name('janis', 'joplin')
assert formatted_name == 'Janis Joplin'
测试如果没有通过
应当修改代码而不是测试
添加新的测试
直接test_开头就行了,然后pytest
测试类
各种断言
测试中常用的断言语句
断言 | 用途 |
---|---|
assert a==b | 断言两个值相等 |
assert a!=b | 断言两个值不等 |
assert a | 断言a的布尔求值为True |
assert not a | 断言a的布尔求职为False |
assert element in list | 断言元素在列表中 |
assert element not in list | 断言元素不在列表中 |
一个要测试的类
定义一个类
survey.py
class AnonymousSurvey:
"""收集匿名调查问卷的答案"""
def __init__(self, question):
"""存储一个问题,并为存储答案做准备"""
self.question = question
self.responses = []
def show_question(self):
"""显示调查问卷"""
print(self.question)
def store_response(self, new_response):
"""存储单份调查答卷"""
self.responses.append(new_response)
def show_results(self):
"""显示收集到的所有答卷"""
print("Survey results:")
for response in self.responses:
print(f"- {response}")
language_survey.py(主程序)
from survey import AnonymousSurvey
# 定义一个问题,并创建一个表示调查的 AnonymousSurvey 对象
question = "What language did you first learn to speak?"
language_survey = AnonymousSurvey(question)
# 显示问题并存储答案
language_survey.show_question()
print("Enter 'q' at any time to quit.n")
while True:
response = input("Language: ")
if response == 'q':
break
language_survey.store_response(response)
# 显示调查结果
print("nThank you to everyone who participated in the survey!")
language_survey.show_results()
test_survey.py
from survey import AnonymousSurvey
def test_store_single_response():
"""测试单个答案妥善储存"""
question="What language did you use first?"
language_survey=AnonymousSurvey(question)
language_survey.store_response('English')
assert 'English' in language_survey.responses
def test_store_three_responses():
"""测试用三个行不行"""
question="What language did you use first?"
Language_survey=AnonymousSurvey(question)
responses=['English','Spanish','Mandarin']
for response in responses:
Language_survey.store_response(response)
for response in responses:
assert response in Language_survey.responses
pytest可以指定测试,比如pytest test_survey.py
就只会运行一个测试
使用夹具
对于每一个测试函数都创建实例是很麻烦的
夹具(fixture)可以帮助搭建测试环境,通常意味着创建供多个测试使用的资源
在 pytest 中,要创建夹具,可编写一个使用装饰器 @pytest.fixture
装饰的函数。 装饰器(decorator)是放在函数定义前面的指令。在运行函数前,Python 将该指令应用于函数,以修改函数代码的行为
比如
import pytest
from survey import AnonymousSurvey
@pytest.fixture
def language_survey():
"""可供所有函数使用的AnonoymousSurvey实例"""
qustion="What language did you use first?"
language_survey=AnonymousSurvey(qustion)
return language_survey
def test_store_single_response(language_survey):
"""测试单个答案妥善储存"""
language_survey.store_response('English')
assert 'English' in language_survey.responses
def test_store_three_responses(language_survey):
"""测试用三个行不行"""
responses=['English','Spanish','Mandarin']
for response in responses:
language_survey.store_response(response)
for response in responses:
assert response in language_survey.responses
从上面可以看到,我们的每一个测试函数都包含了一个形参即language_survey
当测试函数的一个形参与应用了装饰器 @pytest.fixture 的函数(夹具)同名时,将自动运行夹具,并将 夹具返回的值传递给测试函数
数据可视化项目
基于Matplotlib和Plotly实现
生成数据
绘制折线图
import matplotlib.pyplot as plt
squares=[1,4,9,16,25]
fig,ax=plt.subplots()
ax.plot(squares)
plt.show()
subplots()
可以在一个图形中绘制一个或多个绘图,上述fig表示生成的一系列绘图构成的整个图形,ax表示图形中的绘图
plot()方法根据给定数据绘制绘图,plot.show()
打开查看器并显示绘图
修改标签文字和线条粗细
import matplotlib.pyplot as plt
squares=[1,4,9,16,25]
fig,ax=plt.subplots()
ax.plot(squares,linewidth=3)
ax.set_title("Square Numbers",fontsize=24)
ax.set_xlabel("Value",fontsize=14)
ax.set_ylabel("Square of Value",fontsize=14)
ax.tick_params(labelsize=14)
plt.show()
linewidth
决定plot()绘制线条的粗细,set_title()绘图指定标题,fontsize指定各种文字大小
set_xlabel和set_ylabel为每条轴设置标题,tick_params()方法设置刻度标记的样式
矫正绘图
plot()默认数值队列第一个数据点对应的x坐标值为0,如果需要修改,可以直接指定
input_values=[1,2,3,4,5]
squares=[1,4,9,16,25]
fig,ax=plt.subplots()
ax.plot(input_values,squares,linewidth=3)
使用内置样式
可以在调用subplots()的代码前面添加plt.style.use(' ')
scatter()绘制散点图
绘制单个点
import matplotlib.pyplot as plt
plt.style.use('_classic_test_patch')
fig, ax = plt.subplots()
ax.scatter(2, 4)
plt.show()
参数设置
import matplotlib.pyplot as plt
plt.style.use('_classic_test_patch')
fig, ax = plt.subplots()
ax.scatter(2, 4, s=200)
# 设置图题并给坐标轴加上标签
ax.set_title("Square Numbers", fontsize=24)
ax.set_xlabel("Value", fontsize=14)
ax.set_ylabel("Square of Value", fontsize=14)
# 设置刻度标记的样式
ax.tick_params(labelsize=14)
plt.show()
要绘制一系列点,可向 scatter() 传递两个分别包含 x 坐标值和 y 坐标值的列表
比如
x_values = [1, 2, 3, 4, 5]
y_values = [1, 4, 9, 16, 25]
ax.scatter(x_values, y_values, s=100)
自动计算数据
通过循环实现
import matplotlib.pyplot as plt
plt.style.use('_classic_test_patch')
x_values=range(1,1001)
y_values=[x**2 for x in x_values]
fig,ax=plt.subplots()
ax.scatter(x_values,y_values,s=10)
# 设置图题并给坐标轴加上标签
ax.set_title("Square Numbers", fontsize=24)
ax.set_xlabel("Value", fontsize=14)
ax.set_ylabel("Square of Value", fontsize=14)
# 设置刻度标记的样式
ax.tick_params(labelsize=14)
plt.show()
定制刻度标记
ax.axis([0, 1100, 0, 1_100_000])
ax.ticklabel_format(style='plain')
定制颜色
修改数据点的颜色可以通过向scatter()传递参数color来实现,比如
ax.scatter(x_values, y_values, color='red', s=10)
或者RGB
ax.scatter(x_values, y_values, color=(0, 0.8, 0), s=10)
三个值分别为红绿蓝,越接近0越深反之越浅
使用颜色映射
颜色映射 (colormap)是一个从起始颜色渐变到结束颜色的颜色序 列。在可视化中,颜色映射用于突出数据的规律
下面的程序基于数据的y值设置颜色
plt.style.use('seaborn')
fig, ax = plt.subplots()
ax.scatter(x_values, y_values, c=y_values, cmap=plt.cm.Blues,
s=10)
c用于把值关联到颜色映射,camp用于告诉python使用何种映射
自动保存绘图
把plt.show()
换成plt.savefig
即可
plt.savefig('squares_plot.png', bbox_inches='tight')
第一个指定文件名,文件将被存到程序所在目录,第二个指定将绘图多余的空白区域裁剪掉
随机游走
创建类
创建一个名为 RandomWalk
的类,这个类需要三个属性:一个是跟踪随机游走 次数的变量,另外两个是列表,分别存储随机游走经过的每个点的 x 坐标值和 y 坐标值
RandomWalk 类只包含两个方法:__init__()
和 fill_walk()
,后者计算随机游走经过的所有点
对于__init__()
方法
默认情况下Matplotlib独立的缩放每一个轴,这会使得水平或者拉伸绘图,所以我们可以使用ax.set_aspect('equal')
来指定两条轴上刻度的间距必须相等
from random import choice
import matplotlib.pyplot as plt
class RandomWalk:
"""一个生成随机游走数据的类"""
def __init__(self, num_points=5000):
"""初始化随机游走的属性"""
self.num_points = num_points
# 所有随机游走都始于(0, 0)
self.x_values = [0]
self.y_values = [0]
def fill_walk(self):
"""计算随机游走包含的所有点"""
# 不断游走,直到列表达到指定的长度
while len(self.x_values) < self.num_points:
# 决定前进的方向以及沿这个方向前进的距离
x_direction = choice([1, -1])
x_distance = choice([0, 1, 2, 3, 4])
x_step = x_direction * x_distance
y_direction = choice([1, -1])
y_distance = choice([0, 1, 2, 3, 4])
y_step = y_direction * y_distance
# 拒绝原地踏步
if x_step == 0 and y_step == 0:
continue
# 计算下一个点的 x 坐标值和 y 坐标值
x = self.x_values[-1] + x_step
y = self.y_values[-1] + y_step
self.x_values.append(x)
self.y_values.append(y)
rw=RandomWalk()
rw.fill_walk()
plt.style.use('_classic_test_patch')
fig,ax=plt.subplots()
ax.scatter(rw.x_values,rw.y_values,s=15)
ax.set_aspect('equal')
plt.show()
模拟多次随机游走
要在不运行程序多次的情况下使用前面的代码模拟多次随机游走可以用while循环
while True:
plt.show()
keep_running=input("make another walk?(y/n):")
if keep_running=='n':
break
设置随机游走图样式
给点着色
使用颜色映射来指出游走中各个点的先后顺序,并删除每 个点的黑色轮廓,让其颜色更加明显,用一个参数c来即可
from random import choice
import matplotlib.pyplot as plt
class RandomWalk:
"""一个生成随机游走数据的类"""
def __init__(self, num_points=5000):
"""初始化随机游走的属性"""
self.num_points = num_points
# 所有随机游走都始于(0, 0)
self.x_values = [0]
self.y_values = [0]
def fill_walk(self):
"""计算随机游走包含的所有点"""
# 不断游走,直到列表达到指定的长度
while len(self.x_values) < self.num_points:
# 决定前进的方向以及沿这个方向前进的距离
x_direction = choice([1, -1])
x_distance = choice([0, 1, 2, 3, 4])
x_step = x_direction * x_distance
y_direction = choice([1, -1])
y_distance = choice([0, 1, 2, 3, 4])
y_step = y_direction * y_distance
# 拒绝原地踏步
if x_step == 0 and y_step == 0:
continue
# 计算下一个点的 x 坐标值和 y 坐标值
x = self.x_values[-1] + x_step
y = self.y_values[-1] + y_step
self.x_values.append(x)
self.y_values.append(y)
while True:
rw=RandomWalk()
rw.fill_walk()
plt.style.use('_classic_test_patch')
fig,ax=plt.subplots()
point_numbers=range(rw.num_points)
ax.scatter(rw.x_values,rw.y_values,c=point_numbers,cmap=plt.cm.Blues,edgecolors='none',s=15)
ax.set_aspect('equal')
plt.show()
keep_running=input("make another walk?(y/n):")
if keep_running=='n':
break
重新绘制七点和终点
加一行
ax.set_aspect('equal')
# 突出起点和终点
ax.scatter(0, 0, c='green', edgecolors='none', s=100)
ax.scatter(rw.x_values[-1], rw.y_values[-1], c='red', edgecolors='none', s=100)
隐藏坐标轴
加一行
# 隐藏坐标轴
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
调整尺寸以适应屏幕
可在 subplots() 调用中 调整 Matplotlib 输出的尺寸
fig, ax = plt.subplots(figsize=(15, 9))
使用Plotly模拟投掷骰子
创建Die类
from random import randint
class Die:
"""表示一个骰子的类"""
def __init__(self,num_sides=6):
"""骰子默认为6面"""
self.num_sides=num_sides
def roll(self):
"""返回一个位于1和骰子面数之间的随机值"""
return randint(1,self.num_sides)
die=Die()
results=[]
for roll_num in range(100):
result=die.roll()
results.append(result)
print(results)
计算各个数字出现频率
from random import randint
class Die:
"""表示一个骰子的类"""
def __init__(self,num_sides=6):
"""骰子默认为6面"""
self.num_sides=num_sides
def roll(self):
"""返回一个位于1和骰子面数之间的随机值"""
return randint(1,self.num_sides)
die=Die()
results=[]
for roll_num in range(1000):
result=die.roll()
results.append(result)
frequencies=[]
for value in range(1,die.num_sides+1):
frequency=results.count(value)
frequencies.append(frequency)
print(results)
print(frequencies)
展示条形图
import plotly.express as px
--snip--
fig=px.bar(x=range(1,die.num_sides+1),y=frequencies)
fig.show()
px.bar()
创建直方图
fig.show()会让Plotly把直方图渲染成HTML文件并在浏览器打开
订制绘图
比如x和y
title="Results of rolling one D6 1000 times"
labels={'x':'Result','y':'Frequency of Result'}
fig=px.bar(x=range(1,die.num_sides+1),y=frequencies,title=title,labels=labels)
更改绘图
update_layout()
发给发可以对创建的图形做修改
例如fig.update_layout(xaxis_dtcik=1)
调用update_layout()
方法传递参数指定x轴刻度标记的间距
保存图形
例如fig.write_html('***.html')
只提供文件名可以被保存到py所在的目录中
下载数据
CSV文件格式
要在文本文件中存储数据,最简单的方式是将数据组织为一系列 以逗 号分隔的值 (comma-separated values,CSV)并写入文件。这样的 文件称为 CSV 文件。
解析CSV文件头
from pathlib import Path
import csv
from datetime import datetime
import matplotlib.pyplot as plt
path = Path('sitka_weather_2021_simple.csv')
lines = path.read_text().splitlines()
reader = csv.reader(lines)
header_row = next(reader)
# Extract dates and high temperatures.
dates, highs = [], []
for row in reader:
current_date = datetime.strptime(row[2], '%Y-%m-%d')
high = int(row[4])
dates.append(current_date)
highs.append(high)
# Plot the high temperatures.
plt.style.use('seaborn')
fig, ax = plt.subplots()
ax.plot(dates, highs, color='red')
# Format plot.
ax.set_title("Daily High Temperatures, 2021", fontsize=24)
ax.set_xlabel('', fontsize=16)
fig.autofmt_xdate()
ax.set_ylabel("Temperature (F)", fontsize=16)
ax.tick_params(labelsize=16)
plt.show()
csv.reader()
函数接收包含CSV文件中各行的列表
以reader对象为参数的时候,next()返回文件中的下一行(从文件开头开始),上述只调用了一次而且是首次调用,因此得到的是文件第一行(包含文件头)
提取并处理数据
搞一个空列表然后for
for row in reader:
high = int(row[4])
highs.append(high)
datetime模块
可以使用datetime模块中的strptime()方法,第一个实参是包含日期的字符串,第二个告诉python设置日期的格式
参数 | 含义 |
---|---|
%A | 星期几 |
%B | 月份名 |
%m | 用数表示的月份 |
%d | 用数表示的月份中的一天 |
%Y | 四位数的年份 |
%y | 两位数的年份 |
%H | 24小时制的小时数 |
%I | 12小时制的小时数 |
%p | am或pm |
%M | 分钟数 |
%S | 秒数 |
填充两条线中间的区域
# 根据最低和最高温度绘图
plt.style.use('seaborn')
fig, ax = plt.subplots()
ax.plot(dates, highs, color='red', alpha=0.5)
ax.plot(dates, lows, color='blue', alpha=0.5)
ax.fill_between(dates, highs, lows, facecolor='blue', alpha=0.1)
ps:alpha为0表示完全透明,1表示完全不透明
TuSahre
初始化过程
import tushare as ts
ts.set_token('cb231d6532663f88a50370b94b9034dcf1479cb5a7a2f114eb5e0e50')
pro=ts.pro_api()
只能调日线的数据
时间序列数据
常用参数:
trade_date | ts_code |
---|---|
交易日期 | 证券代码 |
部分个股历史数据建议使用ts_code
获取所有可以用trade_date
参数的传入量是四位数年二位数月二位数日
Comments | NOTHING