前言
-
python版本为3.10.11
-
代码块中注释均为中文编码
-
代码块中 #-- 为控制台输出内容
环境
Python下载
Pycharm下载
官网地址:JetBrains: Essential tools for software developers and teams
一.字面量
1.1 常用的六种字面量
数字(Number)
整数(int)
浮点数(float)
复数(complex)
字符串(String)
列表(List)
元组(Tuple)
集合(Set)
字典(Dictionary)
布尔(Bool)
1.2 数据类型
常用的三个:
字符串(string)
整型(int)
浮点型(float)
查看数据类型:type()
print(type(666))
print(type("666"))
print(type(666.0))
#-- <class 'int'>
#-- <class 'str'>
#-- <class 'float'>
int_type = type("789")
print(int_type)
#-- <class 'str'>
name = "123"
name_type = type(name)
print(name_type)
#-- <class 'str'>
1.3 类型转换
int(x)
float(x)
str(x)
# 整型转字符串
num_str = type(str(666))
print(num_str)
#-- <class 'str'>
# 整型转浮点数
num_float = type(float(666))
print(num_float)
#-- <class 'float'>
# 字符串转整型
str_int = type(int("666"))
print(str_int)
#-- <class 'int'>
# 字符串转浮点数
str_float = type(float("666.5"))
print(str_float)
#-- <class 'float'>
# 浮点数转整型
float_int = type(int(666.55))
print(float_int)
#-- <class 'int'>
# 浮点数转字符串
float_str = type(str(666.55))
print(float_str)
#-- <class 'str'>
1.4 字符串扩展
单引号 name = 'zzb'
双引号 name = "zzb"
三引号 三引号支持换行
字符串拼接 :
print("zzb" + "大帅锅")%s(%表示我要占位 s表示将变量转成字符串放入占位的地方)
%s 转成字符串放入占位位置
%d 转成整数放入占位位置
%f 转成浮点数放入占位位置
name = """1q23123
12321321321321321"""
print(name)
#-- 1q23123
#-- 12321321321321321
name = "'黑马'"
print(name)
#-- '黑马'
name = "\"123\""
print(name)
#-- "123"
# 字符串拼接
print("zzb" + "大帅锅")
#-- zzb大帅锅
name = "zzb"
status = "%s学习" % name
print(status)
#-- zzb学习
phone = "1388888"
print("%s的电话是%s" % (name, phone))
#-- zzb的电话是1388888
1.5 格式化字符串的精度控制
m.n:
m:控制宽度,要求是数字(宽度小于数字自身时宽度不生效)
n:控制精度,要求是数字,会进行小数的四射五入
示例:
%5d 表示将整数的宽度控制在5位
%5.2f 表示将宽度控制在5位,精度设置为2
%.2f 表示不限制宽度,只限制精度为2
price = 19.9
print("今天的价格是:%f" % price)
#-- 今天的价格是:19.900000
num = 5
print("限制宽度:%5d" % num) # 限制宽度导致5前面有4个空格
#-- 限制宽度: 5
print("限制宽度为5,精度为3:%7.3f" % price) # 限制宽度为7 所以19.900 前有一个空格(.占据一个宽度) 限制精度为3 所以.后有三位
#-- 限制宽度为5,精度为3: 19.900
print("只限制精度为2:%.2f" % price) # 限制精度为2 所以.后有两位
#-- 只限制精度为2:19.90
字符串格式化快速写法:
f"{变量} {变量}"
name = "123"
year = 2000
price = 19.9
print(f"{name},生产于{year},价格是{price}")
#-- 123,生产于2000,价格是19.9
二.变量
格式: 变量名 = 变量值
注:变量没有类型
money = 50
print("钱包还剩:", money)
#-- 钱包还剩: 50
money = money - 10
print("钱包还剩:", money)
#-- 钱包还剩: 40
内置函数id()可以获取变量的内存地址
允许多个变量指向同一个值
no1 = 2048
print(id(no1))
no2 = 2048
print(id(no2))
#-- 1685782066160
#-- 1685782066160
三.标识符与关键字
1.标识符:
只允许出现:英文、中文(不推荐使用)、数字、下划线(_),数字不可以开头,区分大小写,不可使用关键字
2.关键字:
区分大小写
3.1 关键字表(不需要记)
and | as | assert | break | class | continue |
---|---|---|---|---|---|
def | del | elif | else | except | finally |
for | from | False | global | if | import |
in | is | lambda | nonlocal | not | None |
or | pass | raise | return | try | True |
while | with | yield |
可以输入查看关键字
import keyword
print(keyword.kwlist)
四.运算符
4.1 运算符
加 +
减 -
乘 *
除 /
取整除 //
取余 %
指数 **
4.2 赋值运算符
=
4.3 复合赋值运算符
加法赋值运算符 +=
减法赋值运算符 -=
乘法赋值运算符 *=
除法赋值运算符 /=
取模赋值运算符 %=
幂赋值运算符 **=
取整除赋值运算符 //=
4.4 比较运算符
1. == 2. != 3. > 4. < 5. >= 6. <=
result = 10 > 5
print(result, type(result))
#-- True <class 'bool'>
result = "zzb" == "zzb2" # 比较两个字符串是否相等
print(result)
#-- False
num = 1
str = "1"
print(num == str)
#-- False
当需要判断一个变量是否介于两个值之间时,可以采用“值1<变量<值2”的形式。
4.5 逻辑运算符
运算符 | 含义 | 用法 |
---|---|---|
and | 逻辑与 | op1 and op2 |
or | 逻辑或 | op1 or op2 |
not | 逻辑非 | not op |
4.6 位运算符
不常用
五.数据输入输出
注意:默认接收的都是字符串
print("请输入姓名:")
name = input()
print("您的姓名是:" + name)
name = input("请输入姓名:")
print("您的姓名是:" + name)
5.1 教你一招
默认情况下print()输出后会自动换行,可以使用逗号“,”进行分隔达到不换行输出。
使用chr()即可通过ASCII码显示字符。
print(a,b,"要么出众,要么出局")
print(chr(65))
六.判断语句
6.1 if语句的基本格式
if 要判断的条件: 条件成立时,要做的事情
if else
if elif else
判断语句的嵌套
num = 1
str = "1"
if num != str:
print(num == int(str))
print(num != str)
# 如果num不等于str则比较num与转换类之后的str,最终输出num不等于str的值
#-- True
#-- True
# if else
age = input("请输入年龄:")
if int(age) >= 18:
print("你是成年人")
else:
print("你是未成年人")
# if elif else
height = int(input("请输入身高:"))
if height <= 120:
print("免费")
elif height >= 150:
print("需要买票,15元")
else:
print("需要买票,10元")
# 判断语句的嵌套
height = int(input("请输入身高:"))
if height >= 120:
if height <= 150:
print("需要买票,10元")
else:
print("需要买票,15元")
else:
print("免费")
七.循环
7.1 while循环
while 条件: 执行语句
import random
# 计算1-100的和
i = 1
sum = 0
while i <= 100:
sum += i
i += 1
print(sum)
#-- 5050
7.1.1 猜数字
# 猜数字
number = random.randint(1, 100)
total = 1
flag = True
while flag:
guessNumber = int(input("请输入猜的数字:"))
if guessNumber == number:
print("恭喜你猜对了,共猜了:%s" % total)
flag = False
else:
if guessNumber > number:
print("猜大了")
else:
print("猜小了")
total += 1
7.1.2 九九乘法表
# 九九乘法表
row = 1
while row <= 9:
col = 1
while col <= row:
print(f"{col} * {row} = {row * col} \t", end='')
col += 1
row += 1
print()
7.2 for循环
for循环(遍历循环) for 临时变量 in 待处理数据集 循环满足条件时执行的代码
1.待处理数据集 称之为 序列 2.序列类型有:字符串 列表 元组
name = "zzb"
for word in name:
print(word)
#-- z
#-- z
#-- b
#计算有多少个 a
name = "itheima is a brand of itcast"
sum = 0
for letter in name:
if letter == "a":
sum += 1
print(sum)
#-- 4
for num in range(5, 11, 2): # 生成一个从5开始到11(不包括 11)结束的数字序列,步长为2
print(num)
#-- 5
#-- 7
#-- 9
for num in range(1, 101):
print("第%d天" % num)
for flower in range(1, 11):
print("第%d只花" % flower)
#-- 一天送一只花连续送100天
for i in range(1, 10):
print(i)
break
7.2.1 九九乘法表
#九九乘法表
for row in range(1, 10):
for col in range(1, row + 1):
print(f"{col} * {row} = {row * col}\t", end='')
print()
7.2.2 随机数
range(num) 从0开始获取一个num的序列 不包括num本身 range(3) : [0,1,2]
range(num1,num2) 从num1开始获取一个num的序列 不包括num2本身 range(4,6) : [4,5,]
range(num1,num2,num3) 从num1开始获取一个间隔num3的num序列 不包括num2本身 range(4,9,2) : [4,6,8]
import random
money = 10000
for user in range(1, 21):
if money > 0:
score = int(random.randint(1, 10))
if score <= 5:
print(f"第{user}位,绩效{score}不发放工资!")
continue
else:
money -= 1000
print(f"第{user}位,绩效{score}开始发放工资!账户余额{money}")
else:
print("工资发放完毕")
break
八.函数
定义格式:
def 函数名(传入参数):
"""
注释
"""
函数体
return 返回值
def add(x, y):
"""
求和
:param x: 入参1
:param y: 入参2
:return: 结果
"""
result = x + y
print(f"{x} + {y} = {result}")
add(1, 6)
add("1", "2") # 进行字符串的拼接
add(1.5, 2.6)
#-- 1 + 6 = 7
#-- 1 + 2 = 12
#-- 1.5 + 2.6 = 4.1
函数多返回值:
def test_return_values():
return 10, "你好", True, ["1", "2"]
data1, data2, data3, data4 = test_return_values()
print(f"第一个返回值是:{data1}")
print(f"第二个返回值是:{data2}")
print(f"第三个返回值是:{data3}")
print(f"第四个返回值是:{data4}")
#-- 第一个返回值是:10
#-- 第二个返回值是:你好
#-- 第三个返回值是:True
#-- 第四个返回值是:['1', '2']
函数多种传参方式:
关键字传参
缺省参数
不定长参数
关键字传递
匿名函数
# 关键字传参
def user_info(name, age, sex):
print(f"姓名:{name},年龄:{age},性别:{sex}")
user_info("张三", 12, "男")
user_info(name="李四", age=15, sex="女")
user_info(age=15, name="王五", sex="女")
user_info("张三", age=12, sex="男") # 位置参数必须在前
# 缺省参数
def default_user_info(name, age, sex="男"):
print(f"姓名:{name},年龄:{age},性别:{sex}")
default_user_info("zzb", "45")
# 不定长参数
def length_user_info(*args): # args 元组类型
print(args)
length_user_info("zzb1", "zzb2")
# 关键字传递
def key_user_info(**kwargs):
print(kwargs)
key_user_info(name="张三", age=12)
# 匿名函数
# 函数作为参数传递
def test_func(compute):
result = compute(1, 2)
print(result)
def compute(x, y):
return x + y
test_func(compute) # 只是计算逻辑的传递,不是值的传递
# lambda匿名函数——只可使用一次
test_func(lambda x, y: x + y)
8.1 数学运算类内置函数
函数 | 介绍 |
---|---|
abs(x) | 求绝对值 参数可以是整数,也可以是复数 若参数是复数,则返回复数的模 |
oct(x) | 将一个数字转换为八进制字符串 |
hex(x) | 将整数x转换为十六进制字符串 |
chr(x) | 返回整数x对应的ASCII字符 |
ord(x) | 返回x对应的ASCII码值 |
复数:形如$(a + bi) (a, b \in \mathbb{R})$的数叫复数
复数的模:指复数在复平面上到原点的距离。$\sqrt{a^2 + b^2}$
九.数据容器
列表(list)
元组(tuple)
字符串(string)
集合(set)
字典(dict)
9.1 列表
字面量:[元素1,元素2,·······]
定义变量:变量名称 = [元素1,元素2,·······]
定义空列表: 变量名称 = [] 变量名称 = list()
name_list = ['张三', '李四', 1, 1.2, [1, 2, 3]] # 列表可以嵌套列表
print(type(name_list))
#-- <class 'list'>
for name in name_list:
print(name, type(name))
#-- 张三 <class 'str'>
#-- 李四 <class 'str'>
#-- 1 <class 'int'>
#-- 1.2 <class 'float'>
#-- [1, 2, 3] <class 'list'>
# 下标索引
print(name_list[0])
#-- 张三
# 获取元素的下标
print(name_list.index(1)) # 获取元素1的下标 从0开始
#-- 2
# 反向索引
print(name_list[-1]) # 从-1开始 及最后一个元素
#-- [1, 2, 3]
# 修改索引位置的元素值
name_list[0] = "王五"
print(name_list)
#-- ['王五', '李四', 1, 1.2, [1, 2, 3]]
# 在指定索引位置插入元素
name_list.insert(0, "best") # 索引后的元素索引值加1
print(name_list)
#-- ['best', '张三', '李四', 1, 1.2, [1, 2, 3]]
# 追加元素 追加到尾部
name_list.append("Japan.txt")
print(name_list)
#-- ['张三', '李四', 1, 1.2, [1, 2, 3], 'Japan.txt']
# 把其他容器的内容追加到列表尾部
insert_list = ["qwe", "asd"]
name_list.extend(insert_list)
print(name_list)
#-- ['张三', '李四', 1, 1.2, [1, 2, 3], 'qwe', 'asd']
# 删除元素 (1) 直接删除指定索引的元素
del name_list[0]
print(name_list)
#-- ['李四', 1, 1.2, [1, 2, 3]]
# 删除元素 (2)删除指定索引的元素,返回被删除的元素
name_list.pop(0)
print(name_list)
#-- 张三
#-- ['李四', 1, 1.2, [1, 2, 3]]
# 删除元素在列表中的第一个匹配项
name_list.remove("李四")
print(name_list)
#-- ['张三', 1, 1.2, [1, 2, 3]]
# 清空列表
name_list.clear()
print(name_list)
#-- []
# 遍历列表
index = 0
while index < len(name_list):
print(name_list[index])
index += 1
for element in name_list:
print(element)
9.2 元组
元组(一旦定义完成,不允许修改) 字面量:(元素1,元素2,·······) 定义变量:变量名称 = (元素1,元素2,·······) 定义空列表: 变量名称 = () 变量名称 = tuple() 注意: 单个元组必须以逗号(,)结尾,否则会认定为字符串
t1 = ('nihao', 'python', 'nihao')
print(t1, type(t1))
#-- ('nihao', 'python', 'nihao') <class 'tuple'>
t2 = ('你好',) # 注意这里必须加, 如果不加会默认为字符串
print(t2, type(t2))
#-- ('你好') <class 'tuple'>
# 嵌套元组 与取出内容
t3 = ((1, 2, 3), (4, 5, 6))
num = t3[1][2]
print(num)
#-- 6
# 查找元组中的某个元组坐标
print(t1.index('python'))
#-- 1
# 统计元组中某个元素出现的次数
print(t1.count('nihao'))
#-- 2
# 统计元组中元素的数量
print(len(t1))
#-- 3
9.3 字符串
# 字符串
str1 = "nihao"
# 下标索引取值
print(str1[0])
#-- n
# 查找特定字符串的下标索引值
print(str1.index("hao"))
#-- 2
# 字符串的替换 不是原来的字符串 是一个新的字符串
print(str1.replace("hao", "ni123"))
#-- nini123
# 字符串的分割
str1 = "hello world"
print(str1.split(" "))
#-- ['hello', 'world']
# 字符串的规整操作
# 去除前后空格
str2 = " hello world "
print(str2)
#-- hello world
print(str2.strip())
#-- hello world
# 去除前后指定字符串
str3 = "123 nihao 312"
print(str3.strip("12")) # 其实是去除 单个 1,2
#-- 3 nihao 3
# 统计字符串中 某字符串出现的次数
print(str3.count("3"))
#-- 2
# 统计字符串长度
print(len(str3))
#-- 13
9.4 集合
集合 不支持重复元素 不支持下标索引 支持修改 字面量:{元素1,元素2,·······} 定义变量:变量名称 = {元素1,元素2,·······} 定义空列表: 变量名称 = set()
# 集合添加元素
my_set = {"Hello", "World"}
my_set.add("123")
print(my_set)
#-- {'World', '123', 'Hello'}
# 移除集合中元素
my_set.remove("World")
print(my_set)
#-- {'123', 'Hello'}
# 从集合中随机取出元素,集合本身被修改,元素被移除
print(my_set.pop())
print(my_set)
#-- World
#-- {'Hello', '123'}
# 清空集合,集合本身被清除
my_set.clear()
print(my_set)
#-- set()
# 取出两个集合的差集
my_set1 = {"1", "2", "3"}
my_set2 = {"3", "4", "5"}
print(my_set1.difference(my_set2)) # 取出集合1和集合2的差集(集合1有而集合2没有)原来集合的元素不变
#-- {'2', '1'}
# 清除两个集合的差集
my_set1.difference_update(my_set2) # 对比集合1集合2,在集合1内删除集合2相同的元素 集合1被修改 集合2 不变
print(my_set1)
print(my_set2)
#-- {'1', '2'}
#-- {'4', '3', '5'}
# 两个集合合并成一个
print(my_set1.union(my_set2)) # 得到新的集合,集合1集合2不变
#-- {'2', '4', '3', '1', '5'}
# 统计集合元素数量
print(len(my_set1))
#-- 3
# 集合的遍历
for data in my_set1:
print(data)
#-- 1
#-- 2
#-- 3
十.序列与切片
序列: 内容连续、有序,可使用下标索引的一类数据容器 列表、元组、字符串,均可以视为序列 切片: 从一个序列中,取出一个子序列 语法: 序列[起始下标:结束下标:步长] 起始下标可以为空:视为从头开始 结束下标(不含)可以为空:视为到头结束 步长表示依次取元素的间隔:
步长1表示,一个个取元素
步长2表示,每次跳过1个元素取
步长N表示,每次跳过N-1个元素取
步长为负数表示,反向取(注意,起始下标和结束下标也要反向标记)
my_list = [0, 1, 2, 3, 4, 5, 6]
print(my_list[1:4:1]) # 不含索引为4的元素
#-- [1, 2, 3]
print(my_list[:]) # 从头开始 到头结束 步长为1(省略)
#-- [0, 1, 2, 3, 4, 5, 6]
print(my_list[3:1:-1])
#-- [3, 2]
print(my_list[::2]) # 包含
#-- [0, 2, 4, 6]
print(my_list[::1]) # 包含
#-- [0, 1, 2, 3, 4, 5, 6]
10.1 sort排序
my_list = [["a", 22], ["b", 60], ["c", 45], ["d", 2], ["e", 42]]
def sort(element):
return element[1]
my_list.sort(key=sort, reverse=True)
print(my_list)
# lambda
my_list.sort(key=lambda element: element[1], reverse=False)
print(my_list)
十一.字典
字典 使用{} 存储的元素是键值对 {key:value,key:value,············} my_dict = {key:value,key:value,······} my_dict = {} my_dict = dict()
my_dict = {"张三": 88, "李四": 85}
# 嵌套字典
stu_dict = {
"张三": {
"语文": 80,
"英语": 90
},
"李四": {
"语文": 85,
"英语": 80
}
}
# 查询张三的语文成绩
print(stu_dict["张三"]["语文"])
#-- 80
# 新增元素
my_dict["王五"] = 90
print(my_dict)
#-- {'张三': 88, '李四': 85, '王五': 90}
# 更新元素
my_dict["张三"] = 80
print(my_dict)
#-- {'张三': 80, '李四': 85, '王五': 90}
# 删除元素
score = my_dict.pop("张三")
print(score)
print(my_dict)
#-- 80
#-- {'李四': 85, '王五': 90}
# 清空元素
my_dict.clear()
print(my_dict)
#-- {}
# 获取全部的key
my_dict = {"张三": 88, "李四": 85, "王五": 60}
keys = my_dict.keys()
print(keys)
#-- dict_keys(['张三', '李四', '王五'])
# 遍历字典 1
for key in keys:
print(f"{key}: {my_dict[key]}")
#-- 张三: 88
#-- 李四: 85
#-- 王五: 60
# 遍历字典 2
for key in my_dict:
print(f"{key}: {my_dict[key]}")
# 统计字典内元素数量
num = len(my_dict)
print(num)
#-- 3
十二.文件
12.1 文件的读取(r)
语法:
open(name,mode,encoding)
-
name: 打开的文件名(可以包含文件名的具体路径)
-
mode: 设置打开文件的模式(访问模式):只读、读写、追加等
-
encoding: 编码格式 encoding 的顺序不是第三位,所以不能用位置参数,用关键字参数直接指定
f = open("D:\\study\\python\\pythonProject\\file\\test.txt", "r", encoding="UTF-8")
# read 方法
# 文件对象.read(num) num:从文件中读取的数据长度(单位为字节),如果没有num表示读取全部数据
content1 = f.read(5)
print(content1)
content2 = f.read() # 第二次读会延续上次的位置
print(content2)
f.close()
# readlines 方法
# 按照行的方式读取全部文件内容,返回一个列表,每一行数据为一个元素
f = open("D:\\study\\python\\pythonProject\\file\\test.txt", "r", encoding="UTF-8")
context = f.readlines()
print(f"读取的内容是:{context}")
f.close()
# readline 方法
# 读取一行内容
# for循环读取文件
f = open("D:\\study\\python\\pythonProject\\file\\test.txt", "r", encoding="UTF-8")
for line in f:
print(f"内容是:{line}")
f.close() # 文件关闭
# with open 在操作完成后自动关闭文件
with open("D:\\study\\python\\pythonProject\\file\\test.txt", "r", encoding="UTF-8") as newFile:
for line in newFile:
print(line)
12.2 文件的写入(w)
f = open("D:\\study\\python\\pythonProject\\file\\write.txt", "w", encoding="UTF-8") # w模式
# 如果文件不存在 新建文件
# 如果文件内容不为空 会覆盖原有内容
f.write("zzb") # 并未真正写入文件 积攒在内容中(缓冲区)
# 内容刷新
f.flush() # 真正写入文件
f.close()
12.3 文件的追加(a)
f = open("D:\\study\\python\\pythonProject\\file\\write.txt", "a", encoding="UTF-8") # a模式
# 如果文件不存在 新建文件
# 追加至文件内容后
f.write("zzb") # 并未真正写入文件 积攒在内容中(缓冲区)
# 内容刷新
f.flush() # 真正写入文件
f.close()
十三.异常
try: 可能发生异常的代码 except: 如果出现异常执行的代码
try:
f = open("D:\\study\\python\\pythonProject\\file\\1.txt", "r", encoding="UTF-8")
except FileNotFoundError as e:
print("报错啦")
try:
print(name)
except NameError as e:
print("未定义的变量")
try:
print(name)
0 / 1
except (NameError, ZeroDivisionError) as e:
print("捕获了异常")
try:
print(1)
except NameError as e:
print("出现了异常")
else:
print("没有异常")
try:
f = open("D:\\study\\python\\pythonProject\\file\\write.txt", "r", encoding="UTF-8")
except FileNotFoundError as e:
print("报错啦!文件不存在")
else:
print("文件正常打开!")
finally:
f.close()
13.1 异常的传递
当函数1发生异常,并且没有捕获到异常,异常会传递到下一个函数,直到main函数也没有捕获到异常,程序就会报错
def func1():
print("函数1开始执行")
1 / 0
print("函数1结束执行")
def func2():
print("函数2开始执行")
func1()
print("函数2结束执行")
def func3():
print("函数3开始执行")
func2()
print("函数3结束执行")
func3()
十四.Python模块
模块是一个python文件,能定义函数、类和变量,也可以包含可执行代码 模块导入方式: [from 模块名] import [模块 | 类 | 变量 | 函数 | *] [as 别名]
常用组合形式: import 模块名 from 模块名 import 类、变量、方法等 from 模块名 import * import 模块名 as 别名 from 模块名 import 功能名 as 别名
import time time.sleep(2)
新增模块 my_module1
def my_function(a, b, c):
print("I am a function")
result = (a + b) * c
print(result)
return result
print(__name__)
"""
使用main运行时 __name__ 为 __main__
使用import 导入模块时 __name__ 为 模块名
"""
if __name__ == '__main__':
my_function(1, 2, 3)
__all__ = ['test1'] # 当使用 from my_model2 import * 时 只会引入test1方法
def test1(a, b):
return a + b
def test2(a, b):
return a * b
import my_module1
from my_module2 import *
my_module1.my_function(1, 6, 5)
test1(1, 6)
14.1 样式模块包
-
import 导入
-
from 导入
import my_package.my_module1
import my_package.my_module2
my_package.my_module1.info_print1()
my_package.my_module2.info_input2()
from my_package import my_module1
my_module1.info_print1()
from my_package.my_module2 import info_input2
info_input2()
# 在__init__ 中使用 __all__ = ["my_module1"] 限制 * 形式的模块导入
from my_package import *
my_module1.info_print1()
# my_module2.info_print2()
十五.第三方包的引入
# 安装命令 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 包名
十六.对象
class Student:
name = None
sex = None
age = None
def say_hello(self):
print(f'你好我是{self.name}')
def say_goodbye(self, message):
print(f"再见,{message}")
student_1 = Student()
student_1.name = "张三"
student_1.sex = "男"
student_1.age = 18
student_1.say_hello()
student_1.say_goodbye("zzb")
class User:
name = None
age = None
sex = None
def __init__(self, name, age, sex): # 构造方法
self.name = name
self.age = age
self.sex = sex
def __str__(self): # toString
return f"user类对象,name = {self.name},age = {self.age},sex = {self.sex}"
def __lt__(self, other): # 比较大小 小于 大于(<,>)
return self.age < other.age
def __le__(self, other): # 比较大小 小于等于 大于等于 (<=,>=)
return self.age <= other.age
def __eq__(self, other): # 比较大小 等于(=)
return self.age == other.age
user_1 = User("张三", 12, "男")
user_2 = User("李四", 14, "男")
print(str(user_1))
print(user_1 < user_2)
16.1 私有成员变量、私有成员方法
class Phone:
IMEI = None # 序列号
producer = None # 厂商
# 私有成员变量
__current_voltage = 0.5 # 当前电压
def call_by_5g(self):
print("5G")
# 私有成员方法
def __keep_single_core(self):
print("单核模式运行")
def call_by_5g(self):
if self.__current_voltage >= 1:
print("开启通话")
else:
print("调度开始")
self.__keep_single_core()
phone = Phone()
phone.call_by_5g()
十七.继承
class Phone:
__is_5g_enable = False
def __check_5g(self):
if self.__is_5g_enable:
print("开启5G")
else:
print("5G关闭、使用4G网络")
def call_by_5g(self):
self.__check_5g()
print("正在通话中")
# 继承Phone
class Phone2024(Phone):
face_id = "100001"
def new(self):
print("2024年新功能")
phone = Phone2024()
phone.call_by_5g()
phone.new()
class NFCReader():
nfc_type = "第五代"
def read_nfc_type(self):
print(f"{self.nfc_type}")
class RemoteControl():
rc_type = "红外遥控"
def read_rc_type(self):
print(f"{self.rc_type}")
class MyPhone(NFCReader, RemoteControl):
def print_hello(self):
print(f"你好")
my_phone = MyPhone()
十八.复写
class Phone:
IMEI = None
producer = "ITCAST"
def call_by_5G(self):
print("父类的5G通话")
class MyPhone(Phone):
producer = "ZZB"
def call_by_5G(self):
# 调用父类成员
# 方式一:
Phone.call_by_5G(self)
# 方式二:
print(super().producer)
print("子类的5G通话")
phone = MyPhone()
phone.call_by_5G()
十九.类型注解
var_1: int = 10
var_2: float = 20.22
var_3: str = "3.14"
var_4: bool = True
# my_list: list = [1, 2, 3, 4, 5]
# my_tuple: tuple = (1, 2, 3, 4, 5)
# my_set: set = {1, 2, 3, 4, 5, 6}
# my_dict: dict = {"1": 6}
# my_str: str = "zzb"
# 容器详细注解
my_list: list[int] = [1, 2, 3, 4, 5]
my_tuple: tuple[str, int, bool] = ("zzb", 66, False)
my_set: set[int] = {1, 2, 3, 4, 5, 6}
my_dict: dict[str, int] = {"1": 6}
class Student:
def test(self):
print(f"你好")
stu: Student = Student()
"""
形参注解
"""
def func(x: int, y: int):
print("123")
func(1, 2)
# 对返回值进行注解
def func(x: int, y: int) -> float:
return x * y
result = func(1, 2)
print(result, type(result))
# union 类型联合注解
from typing import Union
my_list_union: list[Union[int, str]] = [1, 2, 3, 4, 5, "123"]
my_dict_union: dict[str, Union[int, str]] = {"age": 6, "sex": "男"}
def func(data: Union[int, str]) -> Union[int, str]:
print(data)
return data
func(789)
func("456")
二十.多态
class Animal:
def speak(self): # 抽象方法
pass # 空实现 由子类决定
class Dog(Animal):
def speak(self):
print("汪汪汪")
class Cat(Animal):
def speak(self):
print("喵喵喵")
def make_noise(animal: Animal):
animal.speak()
dog = Dog()
cat = Cat()
make_noise(dog)
make_noise(cat)
二十一.Spark
spark 大规模数据处理分析引擎 用于计算大量数据
pip install -i Simple Index pyspark
from pyspark import SparkConf, SparkContext
# 创建SparkConf类对象
conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app")
# 基于SparkConf类对象创建SparkContext类对象
sc = SparkContext(conf=conf)
print(sc.version)
sc.stop()
21.1 RDD(弹性分布式数据集)对象
from pyspark import SparkConf, SparkContext
# 创建SparkConf类对象
conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app")
# 基于SparkConf类对象创建SparkContext类对象
sc = SparkContext(conf=conf)
rdd1 = sc.parallelize([1, 2, 3, 4, 5, 6])
rdd2 = sc.parallelize((1, 2, 3, 4, 5, 6, 7))
rdd3 = sc.parallelize("abc")
rdd4 = sc.parallelize({1, 2, 3, 6, 5, 4, 8, 9})
rdd5 = sc.parallelize({"name": "张三", "age": 23})
# 查看RDD 需要用collect()方法
print(rdd1.collect())
print(rdd2.collect())
print(rdd3.collect())
print(rdd4.collect())
print(rdd5.collect())
sc.stop()
21.2 PySpark RDD成员方法
21.2.1 map算子
from pyspark import SparkConf, SparkContext
import os
# 设置环境变量
os.environ['PYSPARK_PYTHON'] = 'D:\\soft\\python-3.10.11\\python.exe'
# 创建SparkConf类对象
conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app")
# 基于SparkConf类对象创建SparkContext类对象
sc = SparkContext(conf=conf)
rdd = sc.parallelize([1, 2, 3, 4, 5])
def func(data):
return data * 10
# 通过map方法将全部数据乘10
# rdd2 = rdd.map(func)
rdd2 = rdd.map(lambda x: x * 10)
# (T) -> U (T):传入参数 U:返回值
print(rdd2.collect())
sc.stop()
21.2.2 flatMap算子
对RDD执行map操作,然后禁用接触嵌套操作
from pyspark import SparkConf, SparkContext
import os
# 设置环境变量
os.environ['PYSPARK_PYTHON'] = 'D:\\soft\\python-3.10.11\\python.exe'
conf = SparkConf().setMaster("local[*]").setAppName("test_flatMap")
sc = SparkContext(conf=conf)
rdd = sc.parallelize(["zzb 666", "python"])
rdd1 = rdd.flatMap(lambda x: x.split(" "))
print(rdd1.collect())
21.2.3 reduceByKey
对KV型RDD,自动按照key分组,然后根据聚合逻辑,完成组内数据(value)的聚合操作
from pyspark import SparkConf, SparkContext
import os
# 设置环境变量
os.environ['PYSPARK_PYTHON'] = 'D:\\soft\\python-3.10.11\\python.exe'
conf = SparkConf().setMaster("local[*]").setAppName("test_reduceByKey")
sc = SparkContext(conf=conf)
rdd = sc.parallelize([('a', 2), ('a', 3), ('b', 2), ('b', 5)])
rdd1 = rdd.reduceByKey(lambda x, y: x + y)
print(rdd1.collect())
21.2.4 filter
过滤想要的数据进行保留
from pyspark import SparkConf, SparkContext
import os
# 设置环境变量
os.environ['PYSPARK_PYTHON'] = 'D:\\soft\\python-3.10.11\\python.exe'
conf = SparkConf().setMaster("local[*]").setAppName("test_reduceByKey")
sc = SparkContext(conf=conf)
rdd = sc.parallelize([1, 2, 3, 4, 5, 6, 7, 8, 9, 1])
rdd1 = rdd.filter(lambda x: x % 2 == 0)
print(rdd1.collect())
21.2.5 distinct(去重)
from pyspark import SparkConf, SparkContext
import os
# 设置环境变量
os.environ['PYSPARK_PYTHON'] = 'D:\\soft\\python-3.10.11\\python.exe'
conf = SparkConf().setMaster("local[*]").setAppName("test_reduceByKey")
sc = SparkContext(conf=conf)
rdd = sc.parallelize([1, 2, 3, 4, 5, 6, 7, 8, 9, 1])
rdd2 = rdd.distinct()
print(rdd2.collect())
21.2.6 sortBy(排序)
ascending : True(升序) False(降序) numPartitions : 用多少分区排序
from pyspark import SparkConf, SparkContext
import os
# 设置环境变量
os.environ['PYSPARK_PYTHON'] = 'D:\\soft\\python-3.10.11\\python.exe'
conf = SparkConf().setMaster("local[*]").setAppName("test_reduceByKey")
sc = SparkContext(conf=conf)
rdd = sc.parallelize([1, 2, 3, 4, 5, 6, 7, 8, 9, 1])
rdd3 = rdd.sortBy(lambda x: x, True, 1)
print(rdd3.collect())
21.2.7 collect
将RDD内容转换为list
from pyspark import SparkConf, SparkContext
import os
# 设置环境变量
os.environ['PYSPARK_PYTHON'] = 'D:\\soft\\python-3.10.11\\python.exe'
conf = SparkConf().setMaster("local[*]").setAppName("test_reduce")
sc = SparkContext(conf=conf)
rdd = sc.parallelize([1, 2, 3, 4, 5, 6, 7])
rdd1 = rdd.collect()
print(rdd1)
21.2.8 reduce
对RDD内容进行自定义聚合
from pyspark import SparkConf, SparkContext
import os
# 设置环境变量
os.environ['PYSPARK_PYTHON'] = 'D:\\soft\\python-3.10.11\\python.exe'
conf = SparkConf().setMaster("local[*]").setAppName("test_reduce")
sc = SparkContext(conf=conf)
rdd = sc.parallelize([1, 2, 3, 4, 5, 6, 7])
rdd1 = rdd.reduce(lambda x, y: x + y)
print(rdd1)
21.2.9 take
取出前N个元素 组成list
from pyspark import SparkConf, SparkContext
import os
# 设置环境变量
os.environ['PYSPARK_PYTHON'] = 'D:\\soft\\python-3.10.11\\python.exe'
conf = SparkConf().setMaster("local[*]").setAppName("test_reduce")
sc = SparkContext(conf=conf)
rdd = sc.parallelize([1, 2, 3, 4, 5, 6, 7])
rdd2 = rdd.take(3)
print(rdd2)
21.2.10 count
计算有多少个元素
from pyspark import SparkConf, SparkContext
import os
# 设置环境变量
os.environ['PYSPARK_PYTHON'] = 'D:\\soft\\python-3.10.11\\python.exe'
conf = SparkConf().setMaster("local[*]").setAppName("test_reduce")
sc = SparkContext(conf=conf)
rdd = sc.parallelize([1, 2, 3, 4, 5, 6, 7])
rdd3 = rdd.count()
print(rdd3)
21.2.11 saveAsTextFile
需要配置Hadoop依赖
from pyspark import SparkConf, SparkContext
import os
# 设置环境变量
os.environ['PYSPARK_PYTHON'] = 'D:\\soft\\python-3.10.11\\python.exe'
os.environ['HADOOP_HOME'] = 'D:\\soft\\hadoop\\hadoop-3.0.0'
conf = SparkConf().setMaster("local[*]").setAppName("test_reduce")
conf.set("spark.default.parallelism", "1") # 设置并行度 方式一
sc = SparkContext(conf=conf)
rdd = sc.parallelize([1, 2, 3, 4, 5, 6, 7])
# 方式二
# 创建rdd时设置
# rdd1 = sc.parallelize([1, 2, 3, 4, 5, 6, 7], numSlices=1)
# rdd2 = sc.parallelize([1, 2, 3, 4, 5, 6, 7], 1)
rdd.saveAsTextFile("D:\\output")
二十二.闭包
# 全局变量account_amount有被修改的风险
account_amount = 0
def atm(num, deposit=True):
global account_amount
if deposit:
account_amount += num
print(f"存款金额:", account_amount)
else:
account_amount -= num
print(f"取款金额:", account_amount)
atm(300)
atm(300)
atm(100, False)
def outer(logo):
def inner(msg):
print(f"<{logo}>{msg}")
return inner
fn1 = outer("zzb1")
fn1("hello")
fn2 = outer("zzb2")
fn2("hello world")
# 修改atm的实现
def account_amount(money=0):
def atm(num, deposit=True):
nonlocal money # 内部函数可以修改外部函数的变量
if deposit:
money += num
print(f"存款金额:", money)
else:
money -= num
print(f"取款金额:", money)
return atm
atm = account_amount()
atm(300)
"""
装饰器
"""
# def outer(func):
# def inner():
# print("我要睡觉了...")
# func()
# print("睡醒了...")
#
# return inner
# def sleep():
# import random
# import time
# print("睡觉中...")
# time.sleep(random.randint(1, 3))
# fn = outer(sleep)
# fn()
# 装饰器的语法糖写法
def outer(func):
def inner():
print("我要睡觉了...")
func()
print("睡醒了...")
return inner
@outer
def sleep():
import random
import time
print("睡觉中...")
time.sleep(random.randint(1, 3))
sleep()
二十三.设计模式
-
单例模式
-
工厂模式
class StrTools:
pass
str_tool = StrTools()
class Person:
pass
class Worker(Person):
pass
class Student(Person):
pass
class Teacher(Person):
pass
class Factory:
def get_person(self, type):
if type == 'worker':
return Worker()
elif type == 'student':
return Student()
elif type == 'teacher':
return Teacher()
else:
return None
factory = Factory()
worker = factory.get_person('worker')
print(worker)
from demo44_1 import str_tool
# 单例模式
s1 = str_tool
s2 = str_tool
print(id(s1))
print(id(s2))
# class str_tool2:
# pass
#
#
# s3 = str_tool2
# s4 = str_tool2
#
# print(id(s3))
# print(id(s4))
二十三.多线程
threading.Thread() 参数: group:暂时无用,预留参数 target:执行的目标任务名 args:以元组的方式给执行任务传参 kwargs:以字典方式给执行任务传参 name:线程名,一般不设置
import threading import time def sing(msg): while True: print(msg) time.sleep(1) def dance(msg): while True: print(msg) time.sleep(2) if __name__ == '__main__': dance_thread = threading.Thread(target=dance, args=('我要跳舞',)) sing_thread = threading.Thread(target=sing, kwargs={"msg": "我在唱歌"}) dance_thread.start() sing_thread.start()
二十四.网络变成
24.1 socket 服务端
import socket # 创建 socket对象 socket_server = socket.socket() # 绑定端口 socket_server.bind(('127.0.0.1', 8888)) # 监听 socket_server.listen(1) # 允许链接的数量 # 等待客户端链接 # 返回二元元组 result: tuple = socket_server.accept() # conn = result[0] # 客户端和服务端的链接对象 # address = result[1] # 客户端的地址信息 conn, address = result # accept() 是阻塞的方法 等待连接,如果没有连接 卡在这一行不执行 print(f"接收到连接,信息为:{address}") while True: # 接收客户端发送的信息 # 使用conn 对象接收信息 data = conn.recv(1024).decode("UTF-8") # recv 缓冲区大小 , 返回字节数组 通过decode 转换为字符串 if data == "exit": break print(f"接收到客户端发送的信息为:{data}") # 发送信息给客户端 reply = input("请输入要发送的信息:").encode("UTF-8") conn.send(reply) # 关闭链接 conn.close()
24.2 socket 客户端
import socket # 创建 socket对象 socket_client = socket.socket() socket_client.connect(('127.0.0.1', 8888)) while True: msg = input("请输入要发送的数据:") if msg == "exit": break socket_client.send(msg.encode("utf-8")) data = socket_client.recv(1024) print("服务端回复:", data.decode("utf-8")) socket_client.close()
二十五.正则表达式
-
match 从头开匹配
-
search 搜索整个字符串 从头开始 找到第一个结束
-
findall 查询全部
import re s = "hello world" result = re.match('hello', s) print(result) print(result.span()) print(result.group()) s1 = "hello world hello" result2 = re.search('world', s1) print(result2) s3 = "hello world hello" result3 = re.findall('hello', s3) print(result3) # 元字符匹配 s = "zzb dsg1" result4 = re.findall(r'\d', s) # 字符串带 r 表示转义字符无效 print(result4)
二十六.操作MySQL数据库
from dbutils.pooled_db import PooledDB import pymysql # 创建连接池 pool = PooledDB( creator=pymysql, # 使用pymysql作为数据库驱动 maxconnections=100, # 连接池允许的最大连接数 mincached=10, # 初始化时,连接池中至少创建的空闲的连接 maxcached=10, # 连接池中最多闲置的连接 maxshared=30, # 连接池中最多共享的连接数量 blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待 setsession=[], # 开始会话前执行的命令列表 ping=0, # ping MySQL服务端,检查是否服务可用 host='192.168.1.61', # 本地 port=3306, user='root', password='123456', database='web', charset='utf8', cursorclass=pymysql.cursors.DictCursor ) class load_mysql: def __init__(self): return # 使用连接池 def query_db(sql, params=None): # 从连接池中获取连接 conn = pool.connection() # 获取游标对象 cursor = conn.cursor() try: if params: # 执行sql cursor.execute(sql, params) else: cursor.execute(sql) # 从结果集中获取所有(剩余的)行,返回一个列表。每一行是一个元组,包含了查询结果中的字段值。如果结果集为空,将返回一个空列表。 result = cursor.fetchall() conn.commit() # 提交 return result except Exception as e: conn.rollback() # 回滚 print(f"An error occurred: {e}") return None finally: # 关闭游标和连接 cursor.close() conn.close() # 使用连接池执行插入操作 def insert_db_return_number(sql, params=None): # 从连接池中获取连接 conn = pool.connection() cursor = conn.cursor() try: if params: cursor.execute(sql, params) else: cursor.execute(sql) conn.commit() # 提交事务 return cursor.rowcount # 返回最后修改的行数 except Exception as e: conn.rollback() # 发生错误时回滚事务 print(f"An error occurred: {e}") return None finally: # 关闭游标和连接 cursor.close() conn.close() def insert_db_return_numbers(sql, params=None): conn = pool.connection() cursor = conn.cursor() try: if params: cursor.execute(sql, params) else: cursor.execute(sql) conn.commit() return cursor.rowcount except Exception as e: if "Duplicate entry" in str(e): print("Insertion failed: Duplicate entry") else: print(f"An error occurred: {e}") conn.rollback() return None finally: cursor.close() conn.close() # 使用连接池执行插入操作 def insert_db_return_id(sql, params=None): # 从连接池中获取连接 conn = pool.connection() cursor = conn.cursor() try: if params: cursor.execute(sql, params) else: cursor.execute(sql) conn.commit() # 提交事务 return cursor.lastrowid # 返回最后插入的id except Exception as e: conn.rollback() # 发生错误时回滚事务 print(f"An error occurred: {e}") return None finally: # 关闭游标和连接 cursor.close() conn.close() # 修改返回id def update_db_return_id(sql, params=None): # 从连接池中获取连接 conn = pool.connection() cursor = conn.cursor() try: if params: cursor.execute(sql, params) else: cursor.execute(sql) conn.commit() # 提交事务 return cursor.lastrowid # 返回最后插入行的主键ID except Exception as e: conn.rollback() # 发生错误时回滚事务 print(f"An error occurred: {e}") return None finally: # 关闭游标和连接 cursor.close() conn.close() def update_db_return_number(sql, params=None): # 从连接池中获取连接 conn = pool.connection() cursor = conn.cursor() try: if params: cursor.execute(sql, params) else: cursor.execute(sql) conn.commit() # 提交事务 return cursor.rowcount # 返回最后修改的行数 except Exception as e: conn.rollback() # 发生错误时回滚事务 print(f"An error occurred: {e}") return None finally: # 关闭游标和连接 cursor.close() conn.close() # 使用连接池执行删除操作 def delete_db(sql, params=None): # 从连接池中获取连接 conn = pool.connection() cursor = conn.cursor() try: if params: cursor.execute(sql, params) else: cursor.execute(sql) conn.commit() # 提交事务 return cursor.rowcount # 返回被删除的行数 except Exception as e: conn.rollback() # 发生错误时回滚事务 print(f"An error occurred: {e}") return None finally: # 关闭游标和连接 cursor.close() conn.close()
import connectMySQL goods_id = 1 user_id = 1 sql = "select * from ygh_order where goods_id=%s and user_id=%s" param = (goods_id, user_id) result = connectMySQL.load_mysql.query_db(sql, param) for row in result: print(row)
二十七.常用的GUI框架
27.1 什么是GUI
GUI全称为Graphical User Interface,即图形用户界面,是一种采用图形方式显示的计算机操作用户界面。
27.1.1 常用的GUI框架
-
wxPython
-
Kivy
-
Flexx
-
PyQt
-
Tkinter
-
Pywin32
-
PyGTK
-
pyui4win
27.2 wxPython的使用
27.2.1 安装wxPython
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple wxPython
27.2.2 创建一个wx.App的子类
import wx class App(wx.App): # 初始化 def OnInit(self): frame = wx.Frame(None, title='Hello World') # 创建一个窗口 frame.Show() # 显示窗口 return True if __name__ == '__main__': app = App() # 创建App类的实例 app.MainLoop() # 调用App类的MainLoop()主循环方法
27.2.3 直接使用wx.App
import wx app = wx.App() # 初始化wx.APP类 frame = wx.Frame(None, title='Hello World') # 创建一个窗口 frame.Show() # 显示窗口 app.MainLoop() # 调用App类的MainLoop()主循环方法
上述代码中,wx.App()初始化wx.App类,包含了OnInit()方法。
27.2.4 使用wx.Frame框架
框架是一个容器,用户可以将它在屏幕上任意移动,并可对他进行缩放,它通常包含标题栏、菜单等。wx.Frame是所有框架的父类。当创建wx.Frame的子类时,子类应调用其父类的构造器。wx.Frame构造器语法格式如下:
wx.Frame.__init__(parent, id=-1, title="", pos=wx.DefaultPosition, size=DefaultSize,style=wx.DEFAULT_FRAM_STYLE,name="frame")
参数说明:
paremt:框架的父窗口。如果是顶级窗口,这个值是None。
id:关于新窗口的id号。通常设为-1,让wx.Frame自动生成一个新的id。
title:窗口的标题。
pos:指定这个新窗口的左上角在屏幕中的位置。通常(0,0)是显示器的左上角。(-1,-1)将让系统决定窗口的位置。
size:指定这个窗口的初始尺寸。(-1,-1)将让系统决定窗口的初始尺寸。
style:指定窗口的类型常量。
name:框架内在的名字。可以通过它来寻找这个窗口。
import wx class MyFrame(wx.Frame): def __init__(self, parent, id): wx.Frame.__init__(self, parent, id, title="创建 Frame", pos=(100, 100), size=(300, 300)) if __name__ == '__main__': app = wx.App() # 初始化应用 frame = MyFrame(None, -1) # 实例化MyFrame类,并传递参数 frame.Show() # 显示窗口 app.MainLoop()
27.2.5 常用控件
27.2.5.1 StaticText文本类
在屏幕上绘制纯文本
wx.StaticText(parent, id, label, pos=wx.DefaultPosition, size=DefaultSize, style=0, name="staticText")
wx.StaticText构造函数的参数如下:
-
parent:父窗口部件
-
id:标识符。使用-1可以自动创建一个唯一的标识
-
label:显示在静态控件中的文本内容
-
pos:窗口部件的位置
-
size:窗口部件的尺寸
-
style:样式标记
-
name:对象的名字
import wx class MyFrame(wx.Frame): def __init__(self, parent, id): wx.Frame.__init__(self, parent, id, title="创建 StaticText", pos=(100, 100), size=(600, 400)) panel = wx.Panel(self) # 创建画板 title = wx.StaticText(panel, label="Python 之禅——Tim Peters", pos=(100, 20)) # 创建标题并设置字体 font = wx.Font(16, wx.DEFAULT, wx.FONTSTYLE_NORMAL, wx.NORMAL) title.SetFont(font) # 创建文本 wx.StaticText(panel, label="优美胜于丑陋", pos=(50, 50)) wx.StaticText(panel, label="明了胜于晦涩", pos=(50, 70)) wx.StaticText(panel, label="简洁胜于复杂", pos=(50, 90)) wx.StaticText(panel, label="复杂胜于凌乱", pos=(50, 110)) wx.StaticText(panel, label="扁平胜于嵌套", pos=(50, 130)) wx.StaticText(panel, label="间隔胜于紧凑", pos=(50, 150)) wx.StaticText(panel, label="可读性胜于效率", pos=(50, 170)) if __name__ == '__main__': app = wx.App() # 初始化应用 frame = MyFrame(None, id=-1) # 实例化MyFrame类,并传递参数 frame.Show() # 显示窗口 app.MainLoop()
字体参数说明:
-
pointSize:字体的整体尺寸,单位为磅
-
family:字体
-
style:字体是否倾斜
-
weight:字体的醒目程度
-
underline:仅在Windows下有效,True则加下划线,False无下划线
-
faceName:字体的名称
-
encoding:编码
wx.Font(pointSize, family, style, weight, underline=False, faceName="", encoding=wx.FONTENCODING_DEFAULT)
27.2.5.2 TextCtrl 输入文本类
允许输入单行或多行文本。也可以作为密码输入控件,掩饰所按下的按键。
wx.TextCtrl(parent, id, value="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=0, validator=wx.DefaultValidator, name=wx.TextCtrlNameStr)
参数说明:
-
parent:父窗口部件
-
id:标识符。使用-1可以自动创建一个唯一的标识
-
pos:部件的位置
-
size:部件的尺寸
-
name:对象的名字
-
style:
-
TE_CENTER 控件中的文本居中
-
TE_LEFT 控件中的文本左对齐
-
TE_NOHIDESEL 始终高亮显示
-
TE_PASSWORD 不显示说键入的文本。以*代替
-
TE_PROCESS_ENTER 如果使用了这个样式,那么当用户在控件内按下回车 键时,一个文本输入事件被触发。否则,按键事件内在的由该文本控件或该对话框管理。
-
TE_PROCESS_TAB 如果指定了这个样式,那么通常的字符事件在Tab键按下 时创建(一般意味一个制表符将被插入文本)。否则,tab由对话框来管理,通常是 控件间的切换。
-
TE_READONLY:文本控件为只读,用户不能修改其中的文本。
-
TE_RIGHT:控件中的文本右对齐。
-
-
value:显示在空间中的初始文本
-
validator:过滤数据
import wx class MyFrame(wx.Frame): def __init__(self, parent, id): wx.Frame.__init__(self, parent, id, title="创建 TextCtrl", pos=(100, 100), size=(400, 300)) panel = wx.Panel(self) # 创建画板 # 创建文本和输入框 self.title = wx.StaticText(panel, label="请输入用户名和密码:", pos=(140, 20)) self.label_user = wx.StaticText(panel, label="用户名:", pos=(50, 50)) self.text_user = wx.TextCtrl(panel, pos=(100, 50), size=(235, 25), style=wx.TE_LEFT) self.label_pwd = wx.StaticText(panel, label="密 码:", pos=(50, 90)) self.text_pwd = wx.TextCtrl(panel, pos=(100, 90), size=(235, 25), style=wx.TE_PASSWORD) if __name__ == '__main__': app = wx.App() # 初始化应用 frame = MyFrame(None, id=-1) # 实例化MyFrame类,并传递参数 frame.Show() # 显示窗口 app.MainLoop()
27.2.5.3 Button按钮类
wx.TextCtrl(parent, id, label, pos, size=wx.DefaultSize, style=0, validator, name="button")
参数与TextCtrl 基本相同,其中label 为显示在按钮上的文本。
import wx class MyFrame(wx.Frame): def __init__(self, parent, id): wx.Frame.__init__(self, parent, id, title="创建 TextCtrl", pos=(100, 100), size=(400, 300)) panel = wx.Panel(self) # 创建画板 # 创建文本和输入框 self.title = wx.StaticText(panel, label="请输入用户名和密码:", pos=(140, 20)) self.label_user = wx.StaticText(panel, label="用户名:", pos=(50, 50)) self.text_user = wx.TextCtrl(panel, pos=(100, 50), size=(235, 25), style=wx.TE_LEFT) self.label_pwd = wx.StaticText(panel, label="密 码:", pos=(50, 90)) self.text_pwd = wx.TextCtrl(panel, pos=(100, 90), size=(235, 25), style=wx.TE_PASSWORD) self.button = wx.Button(panel, label="登录", pos=(105, 130)) if __name__ == '__main__': app = wx.App() # 初始化应用 frame = MyFrame(None, id=-1) # 实例化MyFrame类,并传递参数 frame.Show() # 显示窗口 app.MainLoop()
27.2.6 BoxSizer 布局
上述参数都是通过pos参数布置在画板上,有一种更智能的布局方式—sizer(尺寸器)。wxPython提供了5个sizer
sizer名称 | 描述 |
---|---|
BoxSizer | 在一条水平或垂直线上的窗口部件的布局。当尺寸改变时,控制窗口部件改动 |
GridSizer | 一个基础的网格布局 |
FlexGridSizer | 对GridSizer的升级 |
GridBagSizer | 最灵活的成员。使网格中的窗口部件可以水印放置 |
StaticBoxSizer | 标准的BoxSizer带有标题和环线 |
import wx class MyFrame(wx.Frame): def __init__(self, parent, id): wx.Frame.__init__(self, parent, id, title="用户登录", size=(400, 300)) panel = wx.Panel(self) # 创建画板 # 创建文本和输入框 self.title = wx.StaticText(panel, label="请输入用户名和密码:") # 添加容器,容器中空间按纵向排列 vsizer = wx.BoxSizer(wx.VERTICAL) vsizer.Add(self.title, proportion=0, flag=wx.BOTTOM|wx.Top|wx.ALIGN_CENTRE, border=15) panel.SetSizer(vsizer) if __name__ == '__main__': app = wx.App() # 初始化应用 frame = MyFrame(None, id=-1) # 实例化MyFrame类,并传递参数 frame.Show() # 显示窗口 app.MainLoop()
在上述代码中,设置了增加背景控件(wx.Panel),并创建了一个wx.BoxSizer,它带有一个决定它是水平还是垂直的参数(wx.HORIZONTAL或者wx.VERTICAL),默认为水平。然后使用Add()方法将控件加入sizer,最后使用面板的SetSizer()方法设定它的尺寸器。
Add()语法格式如下:
Box.Add(control,proportion,flag,border)
参数说明:
control:要添加的控件
proportion:所添加控件在定义的定位方式所代表方向上,占据的空间比例。如果有3个按钮,它们的比例值分别为0、1和2,它们都已添加到一个宽度为30的水平排列的wx.BoxSizer中,起始宽度都是10。当sizer的宽度从30变成60时,按钮1的宽度保持不变,仍然是10,按钮2的宽度约为20[10+(60-30)×1/(1+2)],按钮3的宽度约为30。
flag:与border结合可以指定边距宽度(可以通过|联合使用)
wx.LEFT:左边距。
wx.RIGHT:右边距。
wx.BOTTOM:底边距。
wx.TOP:上边距。
wx.ALL:上下左右4个边距。
此外,flag参数还可以与proportion参数结合,指定控件本身的对齐(排列)方式,包括以下选项。
wx.ALIGN_LEFT:左边对齐。
wx.ALIGN_RIGHT:右边对齐。
wx.ALIGN_TOP:顶部对齐。
wx.ALIGN_BOTTOM: 底边对齐。
wx.ALIGN_CENTER_VERTICAL: 垂直对齐。
wx.ALIGN_CENTER_HORIZONTAL: 水平对齐。
wx.ALIGN_CENTER:居中对齐。
wx.EXPAND:占据sizer定位方向上所有可用空间。
border:控制所添加控件的边距。
27.2.7 事件处理
27.2.7.1 绑定事件
bt_confirm.Bind(wx.EVT_BUTTON,OnclickSubmit)
参数说明:
wx.EVT_BUTTON:时间类型为按钮类型(wx.EVT_MOTION:移动鼠标,wx.ENTER_WINDOWS、wx.LEAVE_WINDOWS:鼠标进入或离开一个窗口,wx_EVT_MOUSEWHEEL:鼠标滚轮)
OnclickSubmit:方法名。事件发生时执行该方法。
二十八.PyQt框架的使用
28.1 安装PyQt
地址:
https://www.qt.io/download-qt-installer-oss
阿里云盘
下载完成后,进入在线下载器的目录,打开终端执行命令:
.\qt-online-installer-windows-x64-4.8.0.exe --mirror https://mirrors.aliyun.com/qt/
注意版本,下面是两个镜像网站可以替换
清华大学:https://mirrors.tuna.tsinghua.edu.cn/qt/ 中国科学技术大学:https://mirrors.ustc.edu.cn/qtproject/
https://blog.csdn.net/qq_62888264/article/details/132645054?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522172430472116800185863745%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=172430472116800185863745&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-132645054-null-null.142^v100^pc_search_result_base5&utm_term=qt%E5%AE%89%E8%A3%85&spm=1018.2226.3001.4187
二十九.Django Web框架的使用
29.1 Django Web框架的使用
29.1.1 安装Django Web框架
1.使用pip安装
pip install https://pypi.tuna.tsinghua.edu.cn/simple django==2.0 pip install https://mirrors.aliyun.com/pypi/simple/ django==2.0
2.使用vitrualenv
略
3.使用Anaconda
略
29.1.2 创建一个Django项目
-
首先创建一个文件夹
-
在该文件夹中创建environments目录用于放置虚拟环境,然后在命令行中执行
virtualenv D:\study\python\webprojects\environments\django2.0
-
激活环境
D:\study\python\webprojects\environments\django2.0\Scripts\activate
-
使用“django-admin”命令创建一个项目(在需要创建项目的位置运行)
django-admin startproject demo
-
使用Pycharm打开demo项目
目录结构说明
文件 | 说明 |
---|---|
manage.py | 程序执行的入口 |
settings.py | 总配置文件、可以配置App、数据库、中间件、模板等选项 |
urls.py | 默认路由配置文件 |
wsgi.py | 实现WSGI接口的文件,用于处理Web请求 |
db.sqlite3 | 数据库文件,默认使用这种小型数据库 |
-
运行项目
python manage.py runserver
浏览器中输入 http://127.0.0.1:8000/
来访问
-
在Pycharm命令行中执行
python manage.py migrate python manage.py createsuperuser
用户名:admin 密码:123456
重启服务,在浏览器访问http://127.0.0.1:8000/admin
29.2 创建App
推荐使用App来完成不同模块的任务,通过执行下面的命令启用一个应用程序:
python manage.py startapp app1
目录及文件说明
文件 | 说明 |
---|---|
migrations | 执行数据库迁移生成的脚本 |
admin.py | 配置管理后台的文件 |
apps.py | 单独配置添加的每个App的文件 |
models.py | 创建数据库数据模型对象的文件 |
tests.py | 编写测试脚本的文件 |
views.py | 编写视图控制器的文件 |
将已经创建的App添加到settings.py中,然后将其激活,否则目录中App内文件不会生效。
29.3 数据模型(models)
29.3.1 在App中添加数据模型
在app1的models.py中添加如下代码:
from django.db import models # 引入 class Person(models.Model): """ 编写Person模型类,数据模型应该继承与models.Model或其子类 """ # 第一个字段使用models.CharField类型,长度为30 first_name = models.CharField(max_length=30) # 第二个字段使用models.CharField类型,长度为30 last_name = models.CharField(max_length=30)
上面的类在数据库中会创建如下的表:
create table myapp_person( "id" serial not null primary key, "first_name" varchar(30) not null, "last_name" varchar(30) not null );
对于一些公有的字段,可以使用如下的实现方式:
fromdjango.db import models #引入django.db.models模块 class CreateUpdate(models.Model): # 创建抽象数据模型, 同样要继承于models.Model # 创建时间, 使用models.DateTimeField created_at = models.DateTimeField(auto_now_add=True) # 修改时间, 使用models.DateTimeField updated_at = models.DateTimeField(auto_now=True) class Meta: # 元数据,除了字段以外的所有属性 # 设置model为抽象类。指定该表不应该在数据库中创建 abstract = True class Person(CreateUpdate): # 继承CreateUpdate基类 first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) class Order( CreateUpdate): # 继承CreateUpdate基类 order_id = models.CharField(max_length=30, db_index=True) order_desc = models.CharField(max_length=120)
django.db.models提供的常见字段类型:
字段类型 | 说明 |
---|---|
AutoField | 一个id自增的字段,但创建表过程Django会自动添加一个自增的主键 |
BinaryField | 一个保存二进制源数据的字段 |
BooleanField | 一个布尔值字段,应该指明默认值,管理后台默认呈现为CheckBox形式 |
NullBooleanField | 可以为None值的布尔值字段 |
CharField | 字符串字段,必须指明max_length值 |
TextField | 文本域字段 |
DateField | 日期字段 |
DateTimeField | 时间字段 |
EmailField | 邮件字段 |
FileField | 上传文件字段 |
ImageField | 图片上传字段 |
IntegerField | 整数值字段 |
FloatField | 浮点数值字段 |
SlugField | 只保存字母、数字、下划线和连接符,用于生成URL的短标签 |
UUIDField | uuid |
ForeignKey | 外检关系字段 |
ManyTOManyField | 多对多关系字段 |
OneToOneField | 一对一关系字段 |
29.3.2 执行数据库迁移
不使用Django默认的SQLite,切换成MySQL。在项目的settings.py中找到如下配置:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', } }
替换为:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'demo', 'USER':'root', 'PASSWORD':'123456' } }
创建数据库demo
安装数据库驱动:
pip install https://pypi.tuna.tsinghua.edu.cn/simple pymysql
找到D:\study\python\webprojects\environments\demo\demo__init__.py文件,在首行添加如下代码:
import pymysql pymysql.install_as_MySQLdb()
执行以下命令,创建数据表:
python manage.py makemigrations python manage.py migrate
Django会默认按照“APP名称+下划线+模型类名称”的形式创建数据表
注意:
如果出现RuntimeError: 'cryptography' package is required for sha256_password or caching_sha2_password auth methods
需要安装
pip install cryptography
29.3.3 Django数据API
这里所有命令将在Django的交付命令行中执行,在项目根目录下启用交互命令行,执行以下命令:
python manage.py shell
导入数据模型命令:
from app1.models import Person, Order
创建数据:
方法一:
p = Person.objects.create(first_name="huguo",last_name="zhang")
方法二:
p = Person(first_name="huguo",last_name="zhang") p.save()
查询数据:
Person.objects.all()
查询单个数据:
Person.objects.get(first_name="huguo")
查询指定条件数据:
Person.objects.filter(first_name__exact="huguo") # 指定first_name字段值必须为huguo Person.objects.filter(first_name__iexact="hUguo") # 不区分大小写查找值必须为hugo的 Person.objects.filter(id__gt=1) # 查询所有id大于1的 Person.objects.filter(id__lt=100) # 查询所有id小于100的 Person.objects.exclude(created_at__gt=datetime.datetime.now(tz=datetime.timezone.utc)) # 排除所有创建时间大于现在时间的 Person.objects.exclude(first_name__contains="h").order_by('id') # 过滤出所有first_name包含h的,然后按照id排序 Person.objects.exclude(first_name__icontains="h") # 查询所有first_name不含h的
修改查询到的数据,修改之前需要查询对应的数据
p = Person.objects.get(first_name__exact="huguo") p.first_name="john" p.save()
或者使用get_or_create,如果数据存在就修改,不存在就创建
返回一个元组、一个数据对象和一个布尔值,defaults参数是一个字典。当获取数据的时候,default参数里面的值不会被传入,也就是说获取的对象只存在default之外的关键字参数的值
p.is_created = Person.objects.get_or_create( first_name="hugo", defaults={"last_name":"wang"} )
删除数据同样需要先查找到对应的数据,然后进行删除
Person.objects.get(id=1).delete()
29.3.4 管理后台
编辑app1下面的admin.py文件:
from django.contrib import admin from app1.models import Person, Order class PersonAdmin(admin.ModelAdmin): """ 创建PersonAdmin类,继承自admin.ModelAdmin """ # 列表页显示的字段 list_display = ('id', 'first_name', 'last_name', 'created_at', 'updated_at') # 列表页过滤器 list_filter = ('created_at', 'updated_at') # 列表页搜索 search_fields = ('first_name', 'last_name') # 配置只读字段,设置后不可编辑 readonly_fields = ('created_at', 'updated_at') # 绑定Person模型和PersonAdmin类 admin.site.register(Person, PersonAdmin)
29.3.5 路由(urls)
Django的URL路由流程如下:
Django查找全局urlpatterns变量(urls.py)
按照先后顺序,对URL逐一匹配urlpatterns每个元素
找到第一个匹配时停止查找,根据匹配的结果执行对应的处理函数
如果没有找到或者出现异常,Django会进行错误处理
Django支持三种表达式格式,分别如下:
-
精确字符串格式,如”articles/2017/“
一个精确URL匹配一个操作函数:最简单的形式,适合对静态URL的响应:URL字符串不以“/”揩油,但要以“/”结尾
-
Django的转换格式:<类型:变量名>,如
“articles/<int:year>/”
Django转换后的格式是一个URL模板,匹配URL同时在其中获得一批变量作为参数,是一种常用形式,目的是通过URL进行参数获取和传递
格式转换类型 说明 str 匹配除分隔符(/)外的非空字符,默认类型<year>等价于str:year int 匹配0和正整数 slug 匹配字母、数字、横杠、下划线组成的祖父穿,str的子集 uuid 匹配格式化的UUID path 匹配人格非空字符串,包括路径分隔符,是全集 -
Django支持的正则表达式格式,如
“articles/(?p<year>[0-9]{4})/”
使用正则表达式的两种形式:
-
不提取参数:如
re_path(articles/([0-9]{4}))/
,表示4位数字,每一个数字都是0-9的任意数字 -
提取参数:命名形式为
(?p<name>pattern)
-
注意:当网站功能较多是,可以在该功能文件夹中创建一个urls.py文件,将该功能模块下的URL全部卸载该文件里,但是要在全局的urls.py中使用include方法实现URL映射分发
编写URL的三种情况如下:
-
普通URL:
re_path('^index/',view.index),re_path('^home/',view.Home.as_view())
-
顺序传参:
re_path(r'detail-(\d+)-(\d+).html/',views.detail)
-
关键字传参:
re_path(r'^detail-(?P<nid>\d+)-(?P<uid>\d+).html/',views.detail)
推荐使用关键字传参,找到根目录的配置文件夹demo下面的urls.py:
from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('app1/', include('app1.urls')) ]
在app1下面创建一个urls.py文件,并在其中编写属于这个模块的URL规则:
from app1 import views as app1_views from django.urls import path, include urlpatterns = [ path('articles/2003/', app1_views.special_case_2003), path('articles/<int:year>/', app1_views.year_archive), path('articles/<int:year>/<int:month>/', app1_views.month_archive), path('articles/<int:year>/<int:month>/<slug:slug>/', app1_views.article_detail), ]
如果使用正则:
from app1 import views as views from django.urls import re_path, path urlpatterns = [ path('articles/2003/', views.special_case_2003), re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive), ]
在app1中的views.py中编写:
from django.shortcuts import render # Create your views here. from django.http import HttpResponse def special_case_2003(request): return HttpResponse("This is the special case for 2003.") def year_archive(request, year): return HttpResponse("This is the year archive for %s." % year) def month_archive(request, year, month): return HttpResponse("This is the month archive for %s, %s." % (year, month)) def article_detail(request, year, month, slug): return HttpResponse("This is the article detail for %s, %s, %s." % (year, month, slug))
启动,访问
127.0.0.1:8000/app1/articles/2003/
127.0.0.1:8000/app1/articles/2003/12/
127.0.0.1:8000/app1/articles/2003/12/my-day/
29.3.6 表单(forms)
在app1文件夹下新建一个forms.py文件,添加如下代码:
from django import forms class PersonForm(forms.Form): first_name = forms.CharField(max_length=30, label='First Name') last_name = forms.CharField(max_length=30, label='Last Name')
表单类forms.Form有一个is_valid()方法,可以在views.py中验证提交的表单是否符合规则。
from django.shortcuts import render from django.http import HttpResponse, HttpResponseRedirect from app1.forms import PersonForm def get_name(request): if request.method == 'POST': form = PersonForm(request.POST) if form.is_valid(): first_name = form.cleaned_data['first_name'] last_name = form.cleaned_data['last_name'] return HttpResponse('Hello %s %s' % (first_name, last_name)) else: return HttpResponseRedirect('/error/') else: return render(request, 'name.html', {'form': PersonForm()})
在app1下新建templates文件夹,在该文件夹下新建name.html文件
<form action="/app1/get_name/" method="post"> {% csrf_token %} {{ form }} <button type="submit">Submit</button> </form>
{{ form }}是Django模版语法,用来获取页面返回的数据,这个数据是一个PersonForm实例,所以Django就按照规则渲染表单
添加url到app1/urls.py中
path('get_name/', app1_views.get_name),
访问127.0.0.1:8000/app1/get_name/
29.3.7 视图(views)
def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html)
def person_detail(request, pk): try: p = Person.objects.get(pk=pk) except Person.DoesNotExist: raise Http404('Person Does Not Exist') return render(request, 'person_detail.html', {'person': p})
class PersonFormView(View): form_class = PersonForm initial = {'key': 'value'} template_name = "name1.html" def get(self, request, *args, **kwargs): return render(request, self.template_name, {"form": self.form_class(initial=self.initial)}) def post(self, request, *args, **kwargs): form = self.form_class(request.POST) if form.is_valid(): return HttpResponse('Hello1 %s %s' % (form.cleaned_data['first_name'], form.cleaned_data['last_name'])) return render(request, self.template_name, {"form": form})
path('get_name1/', app1_views.PersonFormView.as_view()),
29.3.8 Django模版
在settings.py中配置
TEMPLATES = [ { # 默认模板引擎 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], # 模板文件目录 'APP_DIRS': True, # 默认加载模板目录下的所有模板文件 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
介绍如何使用模板
{% extends "base_generic.html" %} {% block title %}{{ section.title }}{% endblock %} {% block content %} <h1>{{ section.title }}</h1> {% for story in story_list %} <h2> <a href="{{ story.get_absolute_url }}"> {{ story.headline|upper }} </a> </h2> <p>{{ story.tease|truncatewords:"100" }}</p> {% endfor %} {% endblock %}
标签 | 说明 |
---|---|
{% extends "base_generic.html" %} | 扩展一个母模板 |
{% block title %} | 指定母模板中的一段代码块,此处为title,在母模板中定义title代码块,可以在字模版中重写该代码块,block标签必须是封闭的,要由{% endblock %}结尾 |
{{ section.title }} | 获取变量的值 |
{% for story in story_list %}、{% endfor %} | 和for循环相似,必须是封闭的 |
过滤器:
{{value|default:"nothing"}}:用于指定默认值
{{value|length}}:计算返回的列表或字符串长度
{{value|fiesizeformat}}:将数字转为文件大小,如13KB
{{value|truncatewords:30}}:将返回的字符串取固定长度
{{value|lower}}:将返回数据变为小写字母
三十.游戏框架
30.1 初识
30.1.1 安装
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pygame
30.1.2 Pygame常用模块
模块名 | 功能 |
---|---|
pygame.cdrom | 访问光驱 |
pygame.cursors | 加载光标 |
pygame.display | 访问显示设备 |
pygame.draw | 绘制形状、点和线 |
pygame.event | 管理事件 |
pygame.font | 使用字体 |
pygame.image | 加载和存储图片 |
pygame.joystick | 使用游戏手柄或类似的东西 |
pygame.key | 读取键盘按键 |
pygame.mixer | 声音 |
pygame.mouse | 鼠标 |
pygame.movie | 播放视频 |
pygame.music | 播放音频 |
pygame.overlay | 访问高级视频叠加 |
pygame.rect | 管理矩形区域 |
pygame.sndarray | 操作声音数据 |
pygame.sprite | 操作移动图像 |
pygame.surface | 管理图像和屏幕 |
pygame.surfarray | 管理点阵图像数据 |
pygame.time | 管理事件和帧信息 |
pygame.transform | 缩放和移动图像 |
使用pygame创建窗口:
# -*- coding: utf-8 -*- import sys import pygame pygame.init() # 初始化 size = width, height = 800, 600 # 窗口大小 screen = pygame.display.set_mode(size) # 创建窗口 while True: # 检查事件 for event in pygame.event.get(): # 遍历所有事件 if event.type == pygame.QUIT: # 如果单击关闭窗口,则退出 sys.exit() pygame.quit() # 退出
display的常用方法:
方法名 | 功能 |
---|---|
pygame.display.init | 初始化display模块 |
pygame.display.quit | 结束display模块 |
pygame.display.get_init | 如果display模块已被初始化,则返回true |
pygame.display.set_mode | 初始化一个准备显示的界面 |
pygame.display.get_surface | 获取当前的surface对象 |
pygame.display.flip | 更新整个待显示的surface对象到屏幕上 |
pygame.display.update | 更新部分内容显示到屏幕上。如果没有参数,则与flip方法的功能相同 |
加载图片,绘制窗口
# -*- coding: utf-8 -*- import sys import pygame pygame.init() # 初始化 size = width, height = 800, 600 # 窗口大小 screen = pygame.display.set_mode(size) # 创建窗口 color = (255, 255, 255) ball = pygame.image.load(r"D:\study\python\pythonBook\GUI\images\askme.png") ballrect = ball.get_rect() while True: # 检查事件 for event in pygame.event.get(): # 遍历所有事件 if event.type == pygame.QUIT: # 如果单击关闭窗口,则退出 sys.exit() screen.fill(color) screen.blit(ball, ballrect) pygame.display.flip() pygame.quit() # 退出
surface对象常用方法:
方法名 | 功能 |
---|---|
pygame.Suface.blit | 将一幅图像画到另一幅图像上 |
pygame.Suface.convert | 转换图像的像素格式 |
pygame.Suface.convert_alpha | 转换图像的像素格式,包含Apha通道的转换 |
pygame.Suface.fill | 使用颜色填充Suface |
pygame.Suface.get_rect | 获取Suface的矩形区域 |
跳跃的小球完整代码:
# -*- coding: utf-8 -*- import sys import pygame pygame.init() # 初始化 size = width, height = 800, 600 # 窗口大小 screen = pygame.display.set_mode(size) # 创建窗口 color = (255, 255, 255) ball = pygame.image.load(r"D:\study\python\pythonBook\GUI\images\askme.png") new_width = ball.get_width() // 3 new_height = ball.get_height() // 3 ball = pygame.transform.scale(ball, (new_width, new_height)) ballrect = ball.get_rect() speed = [3, 3] # 速度 x、y轴距离 clock = pygame.time.Clock() # 创建时钟 while True: clock.tick(60) # 设置每秒执行60次 # 检查事件 for event in pygame.event.get(): # 遍历所有事件 if event.type == pygame.QUIT: # 如果单击关闭窗口,则退出 sys.exit() ballrect = ballrect.move(speed) if ballrect.left < 0 or ballrect.right > width: speed[0] = -speed[0] if ballrect.top < 0 or ballrect.bottom > height: speed[1] = -speed[1] screen.fill(color) screen.blit(ball, ballrect) pygame.display.flip() pygame.quit() # 退出
三十一.网络爬虫框架
31.1 网络爬虫的常用技术
31.1.1 Python的网络请求
31.1.1.1 urllib模块
通过urlupload()方法,指定url发送网络请求来获取数据。其子模块描述如表:
模块名称 | 描述 |
---|---|
urllib.request | 该模块定义的打开URL的方法和类,你如,身份验证、重定向、Cookie等 |
urllib.error | 主要包含异常类 |
urllib.parse | URL解析、URL引用 |
urllib.robotparser | 用于解析robots.txt文件 |
import urllib.request response = urllib.request.urlopen('http://www.baidu.com') html = response.read() print(html)
使用post请求:
import urllib.request data = bytes(urllib.parse.urlencode({'word': 'hello'}), encoding='utf-8') response = urllib.request.urlopen('https://ys.xuying.vip/', data=data) html = response.read() print(html)
31.1.1.2 urllib3模块
import urllib3 http = urllib3.PoolManager() response = http.request('GET', 'https://www.baidu.com') print(response.data)
post请求:
import urllib3 http = urllib3.PoolManager() response = http.request('POST', 'https://ys.xuying.vip/', fields={'world': 'hello'}) print(response.data)
31.1.1.3 requests模块
import requests response = requests.get("https://www.baidu.com") print(response.status_code) print(response.url) print(response.headers) print(response.cookies) print(response.text) print(response.content)
31.1.2 代理服务
import requests proxy = { 'http': 'http://127.0.0.1:10809', 'https': 'https://127.0.0.1:10809' # 设置代理地址对应的端口号 } response = requests.get('http://www.mingrisoft.com', proxies=proxy) print(response.content)
31.1.3 BeautifulSoup
31.1.3.1 安装
pip install bs4 pip install beautifulsoup4 pip install lxml pip install html5lib
31.1.3.2 使用
import requests from bs4 import BeautifulSoup response = requests.get("https://www.baidu.com") soup = BeautifulSoup(response.content, "lxml") print(soup)
31.2 网络爬虫开发框架
31.2.1 Scrapy
官网:Scrapy | A Fast and Powerful Scraping and Web Crawling Framework
31.2.1.1 搭建Scrapy框架
pip install Twisted pip install Scrapy
在命令行窗口输入“scrapy”,如果没有出现异常或报错信息,则表示安装成功:
安装pywin32模块
pip install pywin32
31.2.1.2 创建项目
在保存项目文件夹下打开命令行窗口,输入
scrapy startproject scrapyDemo
items.py:项目中定义item文件
middlewares.py:项目中middlewares文件
pipelines.py:项目中pipelines文件
settings.py:项目配置文件
31.2.1.3 创建爬虫
首先需要创建一个存放爬虫模块的文件,该文件需要放在spiders文件夹下,代码示例:
import scrapy class QuotesSpider(scrapy.Spider): name = "itcast" # 定义爬虫的名字 def start_requests(self): # 定义爬取的url urls = [ 'https://quotes.toscrape.com/page/1' ] # 获取所有地址 for url in urls: # 发送网络请求 yield scrapy.Request(url=url, callback=self.parse) def parse(self, response): # 获取数据 page = response.url.split("/")[-2] # 根据页数创建文件名 filename = 'quotes-%s.html' % page # 以写入模式打开文件,如果文件不存在则创建 with open(filename, 'wb') as f: # 将数据写入文件中 f.write(response.body) # 打印日志 self.log('Saved file %s' % filename)
然后在命令行输入”scrapy crawl itcast“其中itcast是爬虫名称
或者
import scrapy from scrapy.crawler import CrawlerProcess from scrapy.utils.project import get_project_settings class QuotesSpider(scrapy.Spider): name = "itcast" # 定义爬虫的名字 def start_requests(self): # 定义爬取的url urls = [ 'https://quotes.toscrape.com/page/1' ] # 获取所有地址 for url in urls: # 发送网络请求 yield scrapy.Request(url=url, callback=self.parse) def parse(self, response): # 获取数据 page = response.url.split("/")[-2] # 根据页数创建文件名 filename = 'quotes-%s.html' % page # 以写入模式打开文件,如果文件不存在则创建 with open(filename, 'wb') as f: # 将数据写入文件中 f.write(response.body) # 打印日志 self.log('Saved file %s' % filename) if __name__ == '__main__': # 创建爬虫进程 process = CrawlerProcess(get_project_settings()) # 添加爬虫 process.crawl("itcast") # 运行 process.start()
31.2.1.4 获取数据
import scrapy from scrapy.crawler import CrawlerProcess from scrapy.utils.project import get_project_settings class QuotesSpider(scrapy.Spider): name = "itcast" # 定义爬虫的名字 def start_requests(self): # 定义爬取的url urls = [ 'https://quotes.toscrape.com/page/1' ] # 获取所有地址 for url in urls: # 发送网络请求 yield scrapy.Request(url=url, callback=self.parse) def parse(self, response): for quote in response.xpath(".//*[@class='quote']"): text = quote.xpath('.//*[@class="text"]/text()').extract_first(), author = quote.xpath('.//*[@class="author"]/text()').extract_first(), tags = quote.xpath('.//*[@class="tags"]/a/text()').extract() print(dict(text=text, author=author, tags=tags)) if __name__ == '__main__': # 创建爬虫进程 process = CrawlerProcess(get_project_settings()) # 添加爬虫 process.crawl("itcast") # 运行 process.start()
翻页获取数据
import scrapy from scrapy.crawler import CrawlerProcess from scrapy.utils.project import get_project_settings class QuotesSpider(scrapy.Spider): name = "itcast" # 定义爬虫的名字 def start_requests(self): # 定义爬取的url urls = [ 'https://quotes.toscrape.com/page/1' ] # 获取所有地址 for url in urls: # 发送网络请求 yield scrapy.Request(url=url, callback=self.parse) def parse(self, response): for quote in response.xpath(".//*[@class='quote']"): text = quote.xpath('.//*[@class="text"]/text()').extract_first(), author = quote.xpath('.//*[@class="author"]/text()').extract_first(), tags = quote.xpath('.//*[@class="tags"]/a/text()').extract() print(dict(text=text, author=author, tags=tags)) for herf in response.css('li.next a::attr(href)'): yield response.follow(herf, callback=self.parse) if __name__ == '__main__': # 创建爬虫进程 process = CrawlerProcess(get_project_settings()) # 添加爬虫 process.crawl("itcast") # 运行 process.start()
创建item
定义item,在items.py中添加:
# Define here the models for your scraped items # # See documentation in: # https://docs.scrapy.org/en/latest/topics/items.html import scrapy class ScrapydemoItem(scrapy.Item): text = scrapy.Field() author = scrapy.Field() tags = scrapy.Field() pass
import scrapy from scrapy.crawler import CrawlerProcess from scrapy.utils.project import get_project_settings from scrapyDemo.items import ScrapydemoItem class QuotesSpider(scrapy.Spider): name = "itcast" # 定义爬虫的名字 def start_requests(self): # 定义爬取的url urls = [ 'https://quotes.toscrape.com/page/1' ] # 获取所有地址 for url in urls: # 发送网络请求 yield scrapy.Request(url=url, callback=self.parse) def parse(self, response): for quote in response.xpath(".//*[@class='quote']"): text = quote.xpath('.//*[@class="text"]/text()').extract_first(), author = quote.xpath('.//*[@class="author"]/text()').extract_first(), tags = quote.xpath('.//*[@class="tags"]/a/text()').extract() item = ScrapydemoItem(author=author, tags=tags, text=text) yield item for herf in response.css('li.next a::attr(href)'): yield response.follow(herf, callback=self.parse) if __name__ == '__main__': # 创建爬虫进程 process = CrawlerProcess(get_project_settings()) # 添加爬虫 process.crawl("itcast") # 运行 process.start()
其他内容参考官网:https://docs.scrapy.org/en/latest
pyecharts
from file_define import FileReader, JsonFileReader, TextFileReader from data_define import Record from bar import SalesBar text_file_reader = TextFileReader("D:/study/python/pythonProject/practice/basis/sales/2011年1月销售数据.txt") json_file_reader = JsonFileReader("D:/study/python/pythonProject/practice/basis/sales/2011年2月销售数据JSON.txt") jan_data = text_file_reader.read_file() feb_data = text_file_reader.read_file() # 合并 all_data: list[Record] = jan_data + feb_data # 数据计算 data_dict = {} for record in all_data: if record.date in data_dict: # 当前日期已经有记录 data_dict[record.date] += record.money else: # 没有记录 data_dict[record.date] = record.money sales_bar = SalesBar(data_dict) sales_bar.buildBar()
from pyecharts.charts import Bar from pyecharts.options import * class SalesBar(Bar): def __init__(self, data_dict): self.data_dict = data_dict def buildBar(self) -> Bar: bar = Bar() bar.add_xaxis(list(self.data_dict.keys())) bar.add_yaxis("销售额", list(self.data_dict.values()), label_opts=LabelOpts(is_show=False)) bar.set_global_opts( title_opts=TitleOpts(title="每日销售额") ) bar.render("每日销售额柱状图.html") return bar
练习-疫情统计图
import json from pyecharts.charts import Line from pyecharts.options import TitleOpts, LegendOpts, ToolboxOpts, VisualMapOpts, LabelOpts f_us = open("/practice/pyecharts/epidemic/America.txt", "r", encoding="utf-8") f_ja = open("/practice/pyecharts/epidemic/Japan.txt", "r", encoding="utf-8") f_in = open("/practice/pyecharts/epidemic/India.txt", "r", encoding="utf-8") us_data = f_us.read() # 美国的全部数据 us_data = us_data.replace("jsonp_1629344292311_69436(", "") us_data = us_data[:-2] # 使用切片的方式 ja_data = f_ja.read() ja_data = ja_data.replace("jsonp_1629350871167_29498(", "") ja_data = ja_data[:-2] in_data = f_in.read() in_data = in_data.replace("jsonp_1629350745930_63180(", "") in_data = in_data[:-2] us_dict = json.loads(us_data) ja_dict = json.loads(ja_data) in_dict = json.loads(in_data) us_trend = us_dict['data'][0]['trend'] ja_trend = ja_dict['data'][0]['trend'] in_trend = in_dict['data'][0]['trend'] us_x_data = us_trend['updateDate'][:314] us_y_data = us_trend['list'][0]['data'][:314] ja_y_data = ja_trend['list'][0]['data'][:314] in_y_data = in_trend['list'][0]['data'][:314] line = Line() line.add_xaxis(us_x_data) line.add_yaxis("美国确诊人数", us_y_data, label_opts=LabelOpts(is_show=False)) line.add_yaxis("日本确诊人数", ja_y_data, label_opts=LabelOpts(is_show=False)) line.add_yaxis("印度确诊人数", in_y_data, label_opts=LabelOpts(is_show=False)) line.set_global_opts( title_opts=TitleOpts(title="2020年美日印确诊人数对比图", pos_left="center", pos_bottom="1%") ) line.render() f_us.close() f_ja.close() f_in.close()
网页数据爬取
浏览器插件获取
import requests from lxml import etree import chardet from beyond_time.write_file import write from beyond_time.processing_content import processing_content # 谷歌插件 XPath Helper # url url = 'https://m.ikbook8.com/book/15335057/17117310.html' while True: next_chapter_link = '' # 伪装 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36' } # 发送请求 verify为是否禁用SSL验证 resp = requests.get(url, headers=headers, verify=True) detected_encoding = chardet.detect(resp.content)['encoding'] # print(f"----------获取到的编码为: {detected_encoding}") # 设置编码 resp.encoding = detected_encoding # print(resp.text) # 以字符串的形式显示 e = etree.HTML(resp.text) content = e.xpath('string(//div[@class="reader-main"]/div[@class="content"][2])') title = e.xpath('string(//div[@class="reader-main"]/h1)') btn = e.xpath('string(//div[@class="content"][2]/a[@class="btn btn-info"])') mark = e.xpath('string(//div[@class="content"][2]/a[@class="btn-addbs"])') # 小章节链接 next_page_link = e.xpath('//div[@class="content"][2]/a[@class="btn btn-info"]/@href') # 如果有小章节链接 使用小章节链接,如果没有则使用大章节链接 if next_page_link: next_page_link = next_page_link[0] url = next_page_link print(next_page_link) else: # 清空小章节链接 next_page_link = None # 获取大章节链接 next_chapter_link = e.xpath('//div[@class="section-opt m-bottom-opt"]/a[3]/@href') if next_chapter_link: next_chapter_link = next_chapter_link[0] url = next_chapter_link # print(url) # 内容处理 content = processing_content(content, btn, mark) print(title) print(content) # 写入文件 write(file_name="光阴之外.txt", title=title, content=content) # 如果大章节链接为https://m.ikbook8.com/book/15335057.html视为最后一章,结束循环 if next_chapter_link == 'https://m.ikbook8.com/book/15335057.html': break
def processing_content(content, btn, mark): """ 内容处理 :param content: 需要处理的内容 :param btn: 脚标 :param mark: 标记 :return: 处理后的内容 """ junk_content = "\/阅|读|模|式|内|容|加|载|不|完|整|,退出可阅读完整内容|点|击|屏|幕|中|间可|退|出|阅-读|模|式|." content = content.replace(junk_content, '') # 处理空格 content = content.replace('\xa0', ' ') # 处理标点 if mark != '': content = content.replace(mark, '') # 如果btn不为空,则把content中与btn相同的内容删除 if btn != '': content = content.replace(btn, '') return content
def write(file_name, content, title): """ 写入文件 w 覆盖 a 续写 :param file_name: 文件名 需要加后缀 :param title: 每章节的标题 :param content: 内容 :return: None """ with open(file_name, 'a', encoding='utf-8') as f: f.write(title + '\n\n' + content + '\n\n') f.flush() # 真正写入文件
实例
实例01
根据输入的年份(4位数字,如2003)计算目前的年龄。
from datetime import datetime # 用户输入出生年份 while True: try: input_year = int(input("请输入您的出生年份(例如:2003): ")) break except ValueError: print("输入无效,请输入一个有效的年份!") # 获取当前年份 now = datetime.now() year = now.year # 计算年龄 if input_year > year: print("您还未出生") print(f"您的年龄是 {year - input_year} 岁。")
实例02
使用BoxSizer布局,实现登录界面
# -*- coding:utf-8 -*- import wx class MyFrame(wx.Frame): def __init__(self, parent, id): wx.Frame.__init__(self, parent, id, '用户登录', size=(400, 300)) # 创建面板 panel = wx.Panel(self) # 创建“确定”和“取消”按钮,并绑定事件 self.bt_confirm = wx.Button(panel, label='确定') self.bt_cancel = wx.Button(panel, label='取消') # 创建文本,左对齐 self.title = wx.StaticText(panel, label="请输入用户名和密码") self.label_user = wx.StaticText(panel, label="用户名:") self.text_user = wx.TextCtrl(panel, style=wx.TE_LEFT) self.label_pwd = wx.StaticText(panel, label="密 码:") self.text_password = wx.TextCtrl(panel, style=wx.TE_PASSWORD) # 添加容器,容器中控件横向排列 hsizer_user = wx.BoxSizer(wx.HORIZONTAL) hsizer_user.Add(self.label_user, proportion=0, flag=wx.ALL, border=5) hsizer_user.Add(self.text_user, proportion=1, flag=wx.ALL, border=5) hsizer_pwd = wx.BoxSizer(wx.HORIZONTAL) hsizer_pwd.Add(self.label_pwd, proportion=0, flag=wx.ALL, border=5) hsizer_pwd.Add(self.text_password, proportion=1, flag=wx.ALL, border=5) hsizer_button = wx.BoxSizer(wx.HORIZONTAL) hsizer_button.Add(self.bt_confirm, proportion=0, flag=wx.ALIGN_CENTER, border=5) hsizer_button.Add(self.bt_cancel, proportion=0, flag=wx.ALIGN_CENTER, border=5) # 添加容器,容器中控件纵向排列 vsizer_all = wx.BoxSizer(wx.VERTICAL) vsizer_all.Add(self.title, proportion=0, flag=wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER, border=15) vsizer_all.Add(hsizer_user, proportion=0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=45) vsizer_all.Add(hsizer_pwd, proportion=0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=45) vsizer_all.Add(hsizer_button, proportion=0, flag=wx.CENTER | wx.TOP, border=15) panel.SetSizer(vsizer_all) if __name__ == '__main__': app = wx.App() frame = MyFrame(None, -1) frame.Show() app.MainLoop()
实例03
根据实例02使用事件判断用户登录
# -*- coding:utf-8 -*- import wx class MyFrame(wx.Frame): def __init__(self, parent, id): wx.Frame.__init__(self, parent, id, '用户登录', size=(400, 300)) # 创建面板 panel = wx.Panel(self) # 创建“确定”和“取消”按钮,并绑定事件 self.bt_confirm = wx.Button(panel, label='确定') self.bt_confirm.Bind(wx.EVT_BUTTON, self.OnConfirm) self.bt_cancel = wx.Button(panel, label='取消') self.bt_cancel.Bind(wx.EVT_BUTTON, self.OnCancel) # 创建文本,左对齐 self.title = wx.StaticText(panel, label="请输入用户名和密码") self.label_user = wx.StaticText(panel, label="用户名:") self.text_user = wx.TextCtrl(panel, style=wx.TE_LEFT) self.label_pwd = wx.StaticText(panel, label="密 码:") self.text_password = wx.TextCtrl(panel, style=wx.TE_PASSWORD) # 添加容器,容器中控件横向排列 hsizer_user = wx.BoxSizer(wx.HORIZONTAL) hsizer_user.Add(self.label_user, proportion=0, flag=wx.ALL, border=5) hsizer_user.Add(self.text_user, proportion=1, flag=wx.ALL, border=5) hsizer_pwd = wx.BoxSizer(wx.HORIZONTAL) hsizer_pwd.Add(self.label_pwd, proportion=0, flag=wx.ALL, border=5) hsizer_pwd.Add(self.text_password, proportion=1, flag=wx.ALL, border=5) hsizer_button = wx.BoxSizer(wx.HORIZONTAL) hsizer_button.Add(self.bt_confirm, proportion=0, flag=wx.ALIGN_CENTER, border=5) hsizer_button.Add(self.bt_cancel, proportion=0, flag=wx.ALIGN_CENTER, border=5) # 添加容器,容器中控件纵向排列 vsizer_all = wx.BoxSizer(wx.VERTICAL) vsizer_all.Add(self.title, proportion=0, flag=wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER, border=15) vsizer_all.Add(hsizer_user, proportion=0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=45) vsizer_all.Add(hsizer_pwd, proportion=0, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=45) vsizer_all.Add(hsizer_button, proportion=0, flag=wx.CENTER | wx.TOP, border=15) panel.SetSizer(vsizer_all) def OnConfirm(self, event): """ 确认按钮事件 :param event: :return: """ username = self.text_user.GetValue() password = self.text_password.GetValue() if username == "" or password == "": message = "用户名或密码不能为空" elif username == "admin" and password == "123456": message = "登录成功" else: message = "用户名或密码错误" wx.MessageBox(message, "提示", wx.OK | wx.ICON_INFORMATION) def OnCancel(self, event): """ 取消按钮事件 :param event: :return: """ self.text_user.SetValue("") self.text_password.SetValue("") if __name__ == '__main__': app = wx.App() frame = MyFrame(None, -1) frame.Show() app.MainLoop()
编码规范
-
每个import语句只导入一个模块,尽量避免一次导入多个模块。
-
不要在行尾加";",不要用分号将两条命令放在同一行。
-
建议每行不超过80个字符,如果超过,建议使用小括号“()”将多行内容隐式连接起来,不推荐使用反斜杠“\”进行连接。
-
使用必要的空行可以提高代码的可读性。
-
通常情况下,运算符两侧、函数参数之间、逗号“,”两侧建议使用空格进行分隔。
-
避免在循环中使用+和+=运算符累加字符串。
-
适当使用异常处理结构提高程序容错性,但不能过于依赖异常处理结构,适当的显示判断。
-
模块名尽量短小,并且全部使用小写字母,可以使用下划线分隔。
-
包名尽量短小,并且全部使用小写字母,不推荐使用下划线。
-
类名采取驼峰命名规则。
-
模块内部的类采用下划线“_”+驼峰命名法。
-
函数、类的属性和方法的命名规则同模块类似,也是全部采用小写字母,多个字母之间用下划线“_”分隔。
-
常量命名时全部采用大写字母,可以使用下划线。
-
使用单下划线“_”开头的模块变量或者函数是受保护的,在使用import * from语句从模块中导入时这些变量或者函数不能被导入。
-
使用双下划线“__”开头的实例变量或者方法时私有的。
附录
1.如果使用Python 2.x 默认使用 ASCII 编码,python文件开头需要加上:
# -*- coding: utf-8 -*-
或者
# conding=utf-8