【python教程】-- 入门 | 小甲鱼《零基础入门学Python》教程笔记(知识点详细、源码可复制)全
(考研成功的第一个暑假,2021-6-1开始学习python,之前了解的很少,只知道其与网络爬虫、视频图像识别、树莓派系统有关)说明:首先,这是个小甲鱼python入门教程的笔记。笔记前面部分是根据2019年版教程(【Python教程】《零基础入门学习Python》最新版)记的,章节标题中带有“(NEW:P1~P34 )”字样;后面部分是根据2013版教程(【Python教程】《零基础入门学习Py
(考研成功的第一个暑假,2021-6-1开始学习python,之前了解的很少,只知道其与网络爬虫、视频图像识别、树莓派系统有关)
说明:
首先,这是个小甲鱼python入门教程的笔记。笔记前面部分是根据2019年版教程(【Python教程】《零基础入门学习Python》最新版)记的,章节标题中带有“(NEW:P1~P34 )”字样;后面部分是根据2013版教程(【Python教程】《零基础入门学习Python》)记的,为什么是2013版?看看鱼C论坛就知道了(https://fishc.com.cn/thread-36000-1-1.html),其章节标题中带有“(OLD:P18~P97 )”字样。新版本与老版本有多大差别?不大,至少新版本的P1~P34我都看过,与老版本相比,更新了一些内容,去掉/新增了一些知识点,但知识点改变的部分几乎可以忽略,因为重要知识点一点儿也没变!
另外,本人已有部分C语言基础,过于基本的东西没有记录,但讲到的95%的知识点都有,而且有大量例程源码,因此具有较大参考价值(不大?难道打代码不费时间吗!);虽然教程中推荐IDLE进行编程,但我后来使用的是pycharm,因为这样效率高,能节省很多时间,但这就导致前面的示例代码一般是解释性的,而后面的示例代码是直接可以运行的。
注意,从“🔰类和对象(OLD:P37~P41)”开始,笔记中的例程源码是基于pycharm的,相对小甲鱼老师的在IDE中运行的稍作了修改,包括:
- 增加主程序入口if name == ‘main’:,以快速在pycharm中运行程序
- 将t1 修改为 print('t1 = ', t1) IDLE运行的教程原程序已在下方注释内容中存放
知识点内容增加了其他参考,做了完善。
适用人群:学习小甲鱼python基础课程的同学
笔记整理发布不易,如果对你有帮助,请多多点赞支持啊!
python语言介绍、和学习准备(NEW:P1~P3)
P1 序章
python的应用
- 人工智能、数据分析、科学计算、网络爬虫、自动化开发、自动化部署
python优点
- 强大、快速、兼容性强、入门轻松、开源
python的后台——pypl
- 第三方模块
课程参考书籍
P2 环境搭建和课程介绍
python相关官网
编辑器
- IDLE
- 推荐使用,新手从基础学起
- 交互模式 + 编辑器模式
Hello Word
- print(“Hello Word”)
P3 (IDLE的使用)用python设计第一个游戏
- IDLE的两种模式:交互模式、编辑器模式
示例
""" 用python设计第一个游戏 """
temp = input("猜一个数字:")
guess = int(temp)
if guess == 8:
print("^_^")
print("@_@")
else:
print("*_*")
print("结束,不玩了")
-
设置IDLE字体
- 设置 -> Options -> Configure IDLE -> (推荐使用)Consolas
-
新手注意点:
- 英文标点
- 正确的缩进
- 函数的拼写(BIF内置函数,通过dir(builtins))
🔰变量、字串符、运算符(NEW:P4-P6)
P4 变量和字串符(上)
-
注意
- 变量名不能以数字开头
- 中文字符可以作为变量名
-
字符串
- 单引号
- 双引号
P5 变量和字串符(下)
- 转义字符 +符号 (可用于显示引号中的引号)
- 反斜杠不能放在字符串末尾,使用 r 可以定义为原始字符 如print(r"D:\three\two\one")
-
三单引号、三双引号 -> 此用法无需每行结尾输入\n\ 后才换行
-
字符串的加减
- 相加 -> 拼接
- 相乘 -> 复制
P6 (比较运算符)是时候讲代码了
转换
- guess = int(temp)
比较运算符
💻实践-随机数模块(NEW:P7-P8)
P7-P8改进我们的小游戏
判断
if x<y:
print("")
else:
print("")
循环
while x<y:
print("")
ctrl + c 强制停止运行
跳出
break
跳出一层循环
随机数模块
import random
answer = random.ranint(1 , 10)
- 操作随机数
- random使用当前操作系统的时间作为种子
- 获取种子:random.getstate()
- 使用方法:
- x = random.getstate()
- … #调用随机数
- random.getstate(x)
导入模块
import
BIF
- BIF 就是 Built-in Functions,内置函数。Python 提供了非常丰富的内置函数,不用导入直接调用即可,如print() 、input()
dir(__builtins__)
查看所有BIFhelp(input)
BIF的功能描述。
示例源码
import random # 导入随机数模块
counts = 3
answer = random.randint(1, 10) # 随机生成1~10的数
while counts > 0:
temp = input("请输入:")
guess = int(temp)
if guess == answer:
print("^_^")
print("@_@")
else:
if guess > answer:
print("大了~~")
else:
print("小了!!")
counts = counts - 1
print("游戏结束,不玩啦~~")
🔰数据类型(NEW:P9-P12)
P9-P10 数字类型
整数
- 有无限大的长度(无限大的精度)
浮点数
- 存储有误差
- 精确存储浮点数
- ↓
import decimal
a = decimal.Decimal('0.2')
复数
x = 1 + 2j //形式
x.real //获取实部
x.imag //获取虚部
运算
P11 布尔类型
False
是假
空字符串""
是假
值等于零是假
false =0 ;true=1
逻辑运算符:
P12 短路逻辑和运算符优先级
短路逻辑
- 从左往右,只有当第一个操作数的值无法确定逻辑运算结果时,才对第二个操作数进行求值。(谁影响结果,就把谁扔出来 )
运算符优先级
🔰流程图思维
P13-P14 谋定而后动,知止而有得
(2021-6-7)
流程图+思维导图
思维导图怎么画
- 方法:
- 顶层设计,逐层往下分析。
- 按功能分析
- 按元素分析
- 比如一个小游戏包含哪些元素,这些元素要干什么,怎么样才能实现指定的功能
其他
- Scratch Desktop(图像化编程软件)
- 《零基础入门学习Scratch》(少儿编程)
- 零基础入门学习web开发
🔰if 和while(NEW:P15~P19)
P15~P19 了不起的分支和循环
分支
'''条件语句1'''
if con:
print()
'''条件语句12'''
if a>b:
print()
elif b>c:
print()
else:
print()
print()
/* 另一种条件表达式 */
print(“条件成立”) if ?>? else print("条件不成立")
small = a if a<b else b
printf(small)
/* 条件表达式用法 */
score = 66
level = ("D" if 0 < score < 60 else
'C' if 60 <= score <80 else
'B' if 80 <= score < 90 else
'A' if 90 <= score < 100 else
'S' if score == 100 else
"请输入 0~100 之间的数值^o^")
printf(level)
//利用括号,是程序可以写在多行,比反斜杠更好用
/* 条件嵌套 */
if ?<?:
print("")
else:
if isMale:
print("")
else:
print("")
循环
while cndition:
statement(s)
break 用于退出死循环 ,永久退出
continue 用于结束单次循环,回到开头
/*while else用法*/
while ?<?
print("")
if ?<?
break
aaa
else:
print("程序正常循环结束,未break") //不使用标志位去检测循环的退出情况
for循环
for 变量 in 可迭代对象:
ststement(s)
range()//可生成数字序列
range(stop)
range(start,stop)
range(start,stop,step)
素数检测
for n in range(2, 15):
for x in range(2,n):
if n%x == 0:
print(n, "=", x, "*", n//x)
break
else:
print(n, "是一个素数")
🔰列表、元组、字符串、序列(NEW:P20-P26)
2021.06.24
P20-P26 列表
列表创建
[1, 2, 3, "一", "二", "三"]//不限制类型
rhyme = [1, 2, 3, "一", "二", "三"]
print(rhyme)
列表引用与遍历
print(rhyme)
for each in rhyme:
print(each)
列表索引
//方式一
rhyme[0]
rhyme[1]
rhyme[2]
...
rhyme[5]
//方式二
rhyme[-6]
rhyme[-5]
rhyme[-4]
...
rhyme[-1]
//方式三
length = len(rhyme)
rhyme[length - 1]
列表切片(一次性获取多个元素)
rhyme[0:3] 或 rhyme[:3]
rhyme[3:6] 或 rhyme[3:] //注意,要写到最后一个元素+1 :[3:6]
rhyme[:] //取出全部元素
rhyme[::2] //取出全部步进元素
rhyme[::-2] //取出全部倒序步进元素
[增]添加列表元素
rhyme1 = [1, 2, 3, 4, 5, 6]
//添加一个
rhyme1.append(7)
//添加多个
rhyme1.extend([8, 9, 10]) //extend()方法的参数必须是一个可迭代对象,新内容追加在原列表最后面
//从中间插入添加
rhyme1.insert(位置 , 元素) //如:rhyme1.insert(1 , "一")
- 用法扩展
切片+添加列表元素
s = [1, 2, 3, 4, 5]
s[len(s):] = [6] //相当于s.append()
s[len(s):] = [7, 8, 9] // 相当于s.extend()
[删]删除列表元素
rhyme2 = [1, 2, 3, 4, 5, 6]
//删除指定元素
rhyme2.remove(6) //如果列表中有多个匹配元素,那么只删除第一个;如果指定元素不存在,则会报错
//删除指定位置的元素
rhyme2.pop(5)
//清空
rhyme2.clear()
[改]替换列表元素
rhyme3 = [1, 2, 3, 4, 5, 6]
//替换指定位置元素
rhyme3[2] = "三"
//替换连续几个位置元素
rhyme3[2:3] = ["三", "四"]
[改]列表排序
rhyme4 = [1, 2, 3, 4, 5, 6]
//从小到大
rhyme4.sort()
//从大到小
rhyme4.sort()
rhyme4.reverse() 或者 rhyme4.sort(reverse=True)
[查]列表查找
rhyme5 = [1, 2, 3, 6, 6, 6,6]
//查找某个元素出现的次数
rhyme5.count(6)
//查找某个元素的索引值
rhyme5.index(6)
rhyme5.index(6,4,6) //最后两个元素是开始、结束位置
列表拷贝
.copy()
= rhyme5[ : ]
列表运算
s = [1, 2, 3]
t = [4, 5, 6]
加:s + t
乘:s *3
多维列表(嵌套列表)
- 应用:matrix = [[ ],[ ],[ ]]
- 访问:
for i in matrix:
for each in i:
print(each,end=' ')
print()
- 索引访问
- matrix[][]
- 创建并初始化
A =[0]*3
for i in range(3):
A[i] = [0]*5
浅拷贝与深拷贝
- 浅拷贝
- = .copy 只拷贝外层对象,内层还是引用(引用:y = x)
- =copy.copy(变量)
- 深拷贝
- =copy.deepcopy(变量)
- =copy.deepcopy(变量)
列表推导式
- [
express
for target in iterable] - [
express
for target in iterableif condition
]
P27 元组
- 什么是元组(tuple)?
- 和列表形似,可使用切片,但:
- 用的是圆括号,且圆括号可省略,且最好不要省略
- 内容不可修改
- 只支持查:count 、 index
- 支持嵌套
- 支持迭代
- 支持 + 和 *
- 支持列表推导式
- 和列表形似,可使用切片,但:
- 打包、解包
- 打包:t = (123, ‘aaasss’, 3.14)
- 解包:x, y, z = t
P28~P33 字符串
处理方法1
- .capitalize - 将字符串首字母编程大写
- .casefold - 将所有字符变成小写
- .title - 每个单词首字母大写
- .swapcase - 大小写反转
- upper - 所有变大写
- .lower - 所有变小写
处理方法2 - 左中右对齐
- center(width,fillchar=’’) 左右填充后居中
- ljust(width,fillchar=’’) 填充后左对齐
- rjust(width,fillchar=’’) 填充后右对齐
- zfil(width) 用0填充
处理方法3 - 查找
- count(sub[,start[,end]]) 在指定位置查找sub指定的子字符串出现次数
- find(sub[,start[,end]]) 在指定位置从左往右查下标索引
- rfind(sub[,start[,end]]) 在指定位置从右往左查下表索引
- index(sub[,start[,end]])** **在指定位置…同find,但若查不到会抛出异常
- rindex(sub[,start[,end]])** **在指定位置…同rfind,但若查不到会抛出异常
处理方法4 - 替换
- expandtabs([tabsize=9])
- 如: = code.expendtabs(4)
- replace(old,new,count=-1)
- 将old字符串替换为new字符串
- translate(tabe)
- 按照table中的方法转换
- 如:‘I I ivcc’.translate(str.maketrans(“HIJK”, “1234”))
处理方法5 - 判断
-
startswith(prefix[, start[, end]]) 判断指定参数的子字符串是否出现在字符串开始位置
-
endswith(suffix[, start, end]]) 判断指定参数的子字符串是否出现在字符串结束位置
-
isupper() 判断字符串中所有字母为大写
-
islower() 判断字符串中所有字母为小写
-
istitle() 判断字符串中所有单词开头字母为大写,其余为小写
-
isalpha() 判断字符串中有没有非字母,没有非字母-返回true
-
isascii()
-
isspace() 判断是空格字符串,包括tab \n
-
isprintable() 判断字符是否全部可打印,比如 \n 不可打印,返回false
-
isdecimal() 判断是数字 123-true
-
isdigit() 判断是数字 123-true 22-true
-
isnumeric() 判断是数字 123-true 22-true Ⅰ Ⅱ Ⅲ-true 一二三-true
-
isalnum() isalpha()、isdecimal()、isdigit()、isnumeric()任何一个是true,他就是true
-
isidentifier() 判断是合法标识符
-
判断一个字符串是否是python保留标识符:
-
import keyword
-
keyword.iskeyword(" ")
处理方法6 - 截取 如:比如" 左侧不要留白".lstrip()
- .strip(char-None) 去除左右空白,传入字符串可以以单个字符作为匹配,去去除
- .lstrip(char-None) 去除左侧空白
- .rstrip(char-None) 去除右侧空白
- .removeprefix(prefix) 去除前缀
- .removesuffix(suffix) 去除后缀
处理方法7 - 拆分&拼接
- .partition(sep) 以从左到右指定的字符串为分割位置分割,返回三元组
- .rpartition(sep) 以从右到左指定的字符串为分割位置分割,返回三元组
- .split(sep=None,maxsplit=-1) 以指定字符串为分隔符分割,maxsplit为分割次数
- .rsplit(sep=None,maxsplit=-1)
- .splitlines(keepends=False) 按行(换行符)分割,以列表形式返回 ;keepends=False不包含换行符
- .join(iterable)
"连接符".join((元组或列表))
使用连接符拼接列表或元组中的元素
格式化字符串
location = "北京";year = 2008
"{0} 奥运会举办于 {1} 年,是{1}年".format(location, year)
- 位置索引 :
"{0} 奥运会举办于 {1} 年,是{1}年".format(location, year)
- 关键字索引
"{location} 奥运会举办于 {year} 年,是{year}年".format(location = "北京", ``year = 2008)
- 位置索引+关键字索引
"{location} 奥运会举办于 {year} 年,是{0}年".format(year, location = "北京", ``year = 2008)
- 语法:
- 参数[align ]
- < : 强制左对齐
"{:<}".format(520)
-
: 强制右对齐
- = : 强制将填充放在符号之后数字之前
- ^ : 强制居中
- < : 强制左对齐
- 参数[width]
- 指定字符宽度
"{:<10}".format(520)
- 指定字符宽度
- 索引参数
- 位置索引
"{2:<10}{1:<10}{0:>10}".format(520, 111, 222)
- 位置索引
- 参数[0]
- 使用“0”作为填充
- 参数[fill]
- 填充元素
"{:0<10}".format(520)
- 填充元素
- 参数[sign]
- + : 根据正负数动态添加 + - :
"{:+}{:-}".format(520, -250)
- - : 只在负数前添加 - :
- 空格 : 正数前添加空格,负数前添加 - :
- + : 根据正负数动态添加 + - :
- 参数[groouping_option]
- 千分位分隔符
- ,:
"{:,}".format(1234)
- _ :
"{:_}".format(1234)
- ,:
- 千分位分隔符
- 参数[.precision]
- 精度
"{:.2f}".format(3.1415)
- "
{:.2g}".format(3.1415)
"{:.3}".format("i love ivcc")
- 参数[type]
- 如:
"{:b}".format(80)
- 如:
- 参数[#]
- 如果以二、八、十六进制输出,自动加前缀0b 0o 0x
- 高级玩法
- 将参数以关键字表示和引用
"{:{fill}{align}{width}.{prec}{type}}".format(3.1415, fill="+", align="^", width=10, prec=3, type="g")
- 将参数以关键字表示和引用
- 参数[align ]
f-字符串
- f-string,在字符串前加f,可让程序更简介方便
"{0} 奥运会举办于 {1} 年,是{1}年".format(location, year)
** ->**f"{location} 奥运会举办于 {year} 年,是{year}年"
- 格式化字符串,可以将format()中的数值放在冒号前面。
- 如
"{:.2f}".format(3.1415)
->f"{3.1415:.2f}"
- 如
P34 序列
列表、元组、字符串都是序列,列表是可变序列,元组和字符串是不可变序列
- 加法乘法
- + 拼接
- * 重复
- 可变对象的+*后的id值不变,不变对象+*运算后的id值改变
- 检测id值——同一性运算符
- is :如
x is y
- is not
- is :如
- 判断元素是否包含在某个序列
- in :如
"i" in "ivcc"
- not in
- in :如
- del
- 用于删除一个或多个指定语句、对象
- 如:x = “ivcc”; del x, y
- 用于删除可变序列中的指定元素
- 如:x = [1, 2, 3, 4, 5]
- del x[1:4] ->相当于切片中的 x[1:4] = []
- 如:x = [1, 2, 3, 4, 5]
- 用于删除一个或多个指定语句、对象
- 列表、元组、字符串相互转换
- list() 转换为列表
- tuple() 转换为元组
- str() 转换为字符串
- 函数
- 对比传入参数返回最值
- min() 返回列表中最小元素、字符串中字母编码值
- max()
min(s, defaule="空的序列")
,若序列可能为空,用这种方法
- **
- len() 计算长度
- sum() 计算求和
- start参数 指定求和起始值,从起始值开始加
sum(s, start=100)
- start参数 指定求和起始值,从起始值开始加
- **
- sorted() 从小到大排序,返回全新列表 (注意:.sort()方法会改变原列表)
sorted(t,reverse=true)
sorted(t, key=len)
key可以干预配许算法- len 比较每个元素长度
- reversed() 翻转
- sorted() 从小到大排序,返回全新列表 (注意:.sort()方法会改变原列表)
- 对比传入参数返回最值
2021-7-22, 最新教程已经更新至34,但停更很久了,也不想等更新,太慢
就先看老教程吧
老教程包含了变量和字符串、数据类型、分支循环、列表、元组、字符串、序列,到此接着学
🔰函数、递归(OLD:P18-P25)
P18~P25 函数
参数
- 多个参数逗号隔开:
def 函数(参数1,参数2):
print(参数1, 参数2)
return(true)
- 形参实参
- 形参:定义函数时设置的参数
- 实参:调用函数时传入的参数
- 关键字参数
- 在实参前面加上关键字索引,如:
函数(参数2 = "我是参数2", 参数1 = "我是参数1")
- 功能:防止参数顺序混乱⭐
- 在实参前面加上关键字索引,如:
- 默认参数
- 在形参后面加上参数,如:
def 函数(参数1 = "默认值1",参数2 = "默认值2"):
- 虽然有默认值,但仍然可以在函数调用时给它(形参)重新赋值
- 功能:防止参数漏掉赋值导致错误⭐
- 在形参后面加上参数,如:
- 收集参数
- 把参数前面加上*,如
def 函数(*参数参数,参数1):
print(len(参数参数)) # 可以识别形参的个数
print(参数[索引]) # 以元组形式调用形参中的参数
- 调用↓
函数(1, 2, 3, "ivcc", 参数1 = "love")
- 功能:拓展功能
- 把参数前面加上*,如
函数文档
- 语法:在函数内部用单引号引注的部分
- 查看:有两种方法
函数.__doc__
,不方便查看,因为换行符直接打印出来了help(函数)
,更方便查看,对换行符进行了转意
返回值
- python函数的返回值可以是多个 或 多种类型,如:
def back():
return [1, "二", 3.14]
- 使用方法类似将一组返回值打包成列表 或 元组
局部变量全局变量
- 局部变量local variable ;全局变量global variable
- 全局变量使用要小心
- 如果在函数内部修改全局变量的值,python将会在函数内部建立一个与全局变量一样的局部变量,修改值不影响全局变量
- 如果要在函数内部修改全局变量,可使用
global
关键字,如:数字1 = 5
def 函数():
global 数字1
数字1 = 10
内嵌函数
- 内嵌函数只能在它的父级函数内被调用
-
闭包
- closure
- 如果一个内部函数对父级函数内的变量进行引用,内部函数就被认为是闭包
- 内部函数要改变父级函数中的变量值的话,可以使用列表形式,如x[0]作为变量,因为列表是直接存放在堆里面,不存放在栈里面;也可以使用
nonlocal
关键字,如:nonlocal x
- 用法和global相似
lambda表达式
- 语法:
lambda x : 2 * x + 1
lambda x, y : 2 * x + y
- 调用
g = lambda x : 2 * x + 1
- 特点:
- 将函数形式转化为表达式, 省下了定义函数的过程,使代码更加简洁
- 不需要考虑命名的问题
- 增加了可读性,不用跑去看函数使怎么定义的
- 不占用内存资源
- 注:如果使用函数,则可能(猜想)会一直占用内存资源
- 注:如果使用函数,则可能(猜想)会一直占用内存资源
BIF
- filter过滤器
- 语法:
filter[function or none, iterable]
- 将iterable(可迭代对象,如列表),中的元素带入function中计算,并返回值为真的元素。
- 如果function为none,则返回iterable中的值为真的元素
- 筛选出值为真的元素
- 如:
list(filter(lambda x : x % 2, range(10)))
- 语法:
- map
- 语法:
- 将序列每个元素作为函数的参数进行加工,直到序列的每个元素都加工完毕,返回新序列
- 如:
list(map(lambda x : x % 2, range(10)))
- 语法:
递归
- 啥是递归
- 相当于函数调用自身
- 对栈操作频繁,很消耗时间、空间
- 注意:
- 很危险:如果忘记返回,将会报错
- 怎么调用自身,且有正确返回?
def factorial(n)
if n == 1:
return 1
else:
return n * factorial(n - 1)
以上是一个计算阶乘的例子
P24~25 递归
分治思想
能将递归算法直接的用代码表示出来
如:斐波那契数列
def fab(n):
if n < 1:
print("输入有误!")
return -1
if n ==1 or n == 2:
return 1
else:
return fab(n--1) + fab(n-2)
result = fab(12)
if result != -1:
print(result)
如:汉诺塔解法
def hanoi(n, x, y, z)
if n == 1:
print(x, '-->', z)
else:
hanoi(n-1, x, z, y) # 将n-1个盘子从x移动到y上
print(x, '-->', z) # 将最底下的最后一个盘子从x移动到z上
hanoi(n-1, y, x, z) # 将y上的n-1个盘子移动到z上
n = int(input("请输入汉诺塔层数:"))
hanoi(n, 'X', 'Y', 'Z')
<br />
🔰字典、集合(OLD:P26-P28)
P26~27 字典:当索引不好用时
- 说明
- 字典相当于一个一对一映射类型
- 包含两种元素:键(key)、值(value)
创建
dict = {'键1':'值1', '键2':'值2', '键3':'值3'}
- 键可以是一个整型、字符型、字符串、变量
- 一对键、值称为“项”
- 调用:
dict['键3']
- 创建空的字典:
dict = {}
修改
dict['键3'] = ['值6']
dict['键4'] = ['值4']
– 若键为新的,则在原字典中添加新的项
字典工厂函数
- dict(map)
- map参数:映射类型
- map参数:映射类型
内建函数
- formkeys()
- 使用方法:
dict.fomkeys(s[,v])
- 创建并返回一个新的字典,如:
dict.fokeys((1,2), '新值')
- 使用方法:
访问
- keys()
- 返回字典的键
- 用法:
for eachkey in dict1.keys()
print(eachkey)
- values()
- 用法同keys
- items()
- 用法同keys
- 用元组的形式输出
- get方法
- 用法:
dict1.get(键3)
dict1.get(键3, 没有值)
- 功能
- 能避免直接输出时,遇到空值造成的程序错误
- 能避免直接输出时,遇到空值造成的程序错误
- 用法:
修改
- clear方法
- 清空所有字典的键、值
- 包括清空类似
dict2 = dict1
这样的对其引用的dict2的键、值
- pop方法
dict.pop(键)
- 弹出(移除+返回)一个键及其对应的值
- popitem方法
dict.popitem
- 随机弹出一个项
- setdefault方法
- 弹入一个项
dict.setdefault(键5, 值5)
- update方法
- 更新键对应的元素
dict.update(键5, 值7)
拷贝
- 浅拷贝:
- copy()
- 用法:dict2 = dict1.copy()
- 特点:拷贝出一个新的字典
- copy()
- dict2 = dict1 拷贝
- 两个字典指向同一个id(两个字典是相同的东西)
- 两个字典指向同一个id(两个字典是相同的东西)
P28 集合:在我的世界里你就是唯一
集合的特点
- 集合是无需的
- 集合是不重复的,能把重复的数据清理掉
- 集合不支持索引
集合创建
- ①用一对花括号表示{ , , }
- ②使用set工厂函数
set1 = set([ , , ,])
集合的访问
- 使用for循环,一个个读取
- 使用 in 或 not in 判断一个元素是否在集合中
集合的修改
- 添加元素:
num1.add(要添加的元素)
- 移除元素:
num1.remove(要移除的元素)
不可变集合
不可添加、删除元素
- 创建
mun2 = frozenset([ , , , ])
使用示例
- 去除列表中的重复部分
num1 = list(set(num1))
- 注意:此方法得到的集合是无序的
🔰文件(OLD:P29-32)
P29-30 文件操作
文件打开
- 语法:
file object = open(file_name [, access_mode][, buffering])
- file_name:要访问的文件路径及名称的字符串值。
- access_mode:决定了打开文件的模式:只读,写入,追加等。默认文件访问模式为只读®。
- buffering:
- 值取0,不会有寄存。
- 值取1,访问文件时会寄存行。
- 值取大于1的整数,表明了这就是的寄存区的缓冲大小。
- 值取负值,寄存区的缓冲大小则为系统默认。
f = open('E:\\test1.txt', 'w')
表:python文件打开模式,参考自https://www.runoob.com/python/python-files-io.html
打开模式 | 执行操作 |
---|---|
r | 以只读方式打开文件。文件的指针将会放在文件的开头(默认)。 |
w | 以写入方式打开文件。如果该文件已存在,则打开文件并覆盖原有内容;如果该文件不存在,则创建新文件。 |
x | 写模式,新建一个文件,如果该文件已存在则会报错。 |
a | 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
b | 以二进制模式打开文件。 |
t | 以文本模式打开文件 (默认)。 |
+ | (可读可写)打开一个文件进行更新(可添加到其他模式中使用)。 |
U | 支持通用换行模式(不推荐)。 |
rb | 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。一般用于非文本文件如图片等。 |
r+ | 打开一个文件用于读写。文件指针将会放在文件的开头。 |
rb+ | 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。一般用于非文本文件如图片等。 |
wb | 以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。 |
w+ | 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
wb+ | 以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。 |
ab | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
a+ | 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。 |
ab+ | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。 |
- 直接print(f),会输出一个文件对象:
<_io.TextIOWrapper name='D:\\test1.txt' mode='r' encoding='cp936'>
文件对象方法
文件对象方法 | 执行操作 |
---|---|
f.close() | 关闭文件 |
f.read(size=-1) | 从文件读取size个字符,当未给定size或给定负值时,读取文件指针后剩余所有字符串,然后作为字符串返回 |
f.readline() | 以写入模式打开,如果文件存在,则在末尾追加写入 |
f.write(str) | 将字符串str写入文件 |
f.writelines(seq) | 向文件写入字符串序列seq,seq应该是一个返回字符串的可迭代对象 |
f.seek(offset,from) | 在文件中移动文件指针,从from(0代表文件起始位置,1代表当前位置,2代表文件末尾)偏移offset个字节 |
f.tell() | 返回当前文件中文件指针的位置 |
-
list(f)
能将文件直接转化为列表 -
读取文件对象
for each_line in f:
print(each_line)
- 注意:
文件写入后,关闭文件才能保存写入内容f.write('i love ivcc')
f.close()
文件内容处理
示例代码
def save_file(count, EM, YZ):
# 文件保存
if count != 0:
file_name_EM = '恶魔_' + str(count) + '.txt'
file_name_YZ = '勇者_' + str(count) + '.txt'
EM_file = open(file_name_EM, 'w', encoding='utf-8')
YZ_file = open(file_name_YZ, 'w', encoding='utf-8')
EM_file.writelines(EM)
YZ_file.writelines(YZ)
EM_file.close()
YZ_file.close()
def main_split_file():
EM = []
YZ = []
count = 0
f = open('.\\file内容分割test_file.txt', encoding='utf-8')
for each_line in f:
print(each_line)
if each_line[:6] != '======':
# 按照:分割字符串,保存到序列
if count != 0:
(role, line_spoken) = each_line.split(':', 1)
if role == '恶魔':
EM.append(line_spoken)
if role == '勇者':
YZ.append(line_spoken)
else:
save_file(count, EM, YZ)
EM = []
YZ = []
count += 1
save_file(count, EM, YZ)
f.close()
if __name__ == '__main__':
main_split_file()
P31 文件系统
OS模块
访问文件系统的模块,用来处理文件和目录
OS: Operating System 操作系统
os基本方法
方法名 | 描述 | 示例 |
---|---|---|
os.getcwd() | 返回当前工作目录 | |
os.chdir(path) | 改变当前工作目录 | |
os.listdir(path) | 返回指定path中的文件名、文件夹名(列表形式) | path = "/var/www/html/" dirs = os.listdir( path ) |
os.mkdir(path) | 创建单层目录 如果目录有多级,则创建最后一级, 如果最后一级目录的上级目录有不存在的,则抛异常 如果要创建的目录 已存在,则抛异常 | # 已有目录/temp/home path = "/temp/home" os.mkdir( path, 0755 ) |
os.mknod(filename) | 创建文件 不支持Windows,需以open方法创建 | newf = "old.txt"<br />``nf = open(newf,'w') nf.close() |
os.makedirs(path) | 创建多层目录 如果子目录创建失败或者已经存在,则抛异常 | # 已有目录/temp/home path = "/temp/home/files/a" os.makedirs( path, 0755 ) |
os.remove(path) | 删除文件 如果path 是一个文件夹,则抛异常 | |
os.rmdir(path) | 删除单层目录 如果该目录非空,则抛异常 | |
os.removedirs(path) | 删除多层目录 从子目录到父目录逐层尝试删除,若遇到目录非空则抛异常 | |
os.rename(old, new) | 将文件old重命名为new 可修改文件路径 | os.renames("old.txt","newdir/new.txt") |
os.system(command) | 运行系统的shell命令 | os.system('cmd') os.system('calc') |
os.walk(top) | 遍历top路径以下所有的子目录, 返回一个三元组:(root,dirs,files) - root文件夹的本身的地址 - dirs 是 list ,文件夹中所有的目录的名字(不包括子目录) - files 是 list , 文件夹中所有的文件(不包括子目录) | print(list(os.walk("."))) |
-
注:
- '.'表示当前目录
- '…'表示上一级目录
-
路径操作中常用到的一些定义
- os.curdir 指代当前目录(’.’)
- os.pardir 指代上一级目录(’…’)
- os.sep 输出操作系统特定的路径分隔符(Win下为’\’,Linux下为’/’)
- os.linesep 当前平台使用的行终止符(Win下为’\r\n’,Linux下为’\n’)
- os.name 指代当前使用的操作系统(包括:‘posix’, ‘nt’, ‘mac’, ‘os2’, ‘ce’, ‘java’)
os.path
函数名 | 使用方法 | 示例 |
---|---|---|
os.path.basename(path) | 返回path中的文件名 | |
os.path.dirname(path) | 返回去除path中文件名的路径 | |
os.path.join(path1[, path2[, …]]) | 将path1, path2合成, 返回 "path1\\path2\\[, …]" 从左到右,合成时加 ‘\\’,可’C:\\’ | p1 = os.path.join('path1', 'path1.1', 'path1.1.1') p2 = os.path.join('C:\\\\', 'path1', 'path1.1')<br />print(p1, p2) |
os.path.split(path) | 分割文件名与路径, 返回(f_path, f_name)元组。 如果path中没有文件名,它会将最后一个目录作为文件名分离 | |
os.path.splitext(path) | 分离文件名与扩展名, 返回(f_name, f_extension)元组 | |
os.path.getsize(file) | 返回指定文件大小,单位是字节 | |
os.path.getatime(file) | 返回指定文件最近的访问时间 | (浮点型秒数,可用time模块的gmtime()或localtime()函数换算)gmtime(os.path.getatime(file)) # 国际时间 localtime(os.path.getatime(file)) # 北京时间 |
os.path.getctime(file) | 返回指定文件的创建时间 | |
os.path.getmtime(file) | 返回指定文件最新的修改时间 | |
os.path.exists(path) | 判断指定路径(目录或文件)是否存在 | (返回 True 或 False) |
os.path.isabs(path) | 判断指定路径是否为绝对路径 | |
os.path.isdir(path) | 判断指定路径是否存在且是一个目录 | |
os.path.isfile(path) | 判断指定路径是否存在且是一个文件 | |
os.path.islink(path) | 判断指定路径是否存在且是一个链接 | |
os.path.ismount(path) | 判断指定路径是否存在且是一个挂载点 | |
os.path.samefile(path1, paht2) | 判断path1和path2两个路径目录是否相同 |
P32 永久存储
pickle模块
“Pickling”是将Python对象层次结构转换为二进制字节流的过程,
“unpickling”是反向操作
作用:
- 简化程序,将大量的数据打包(如: 城市字典)成二进制文件,需要使用时调用即可
示例:
import pickle
def dabao():
my_list = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
pickle_file = open('my_list.pkl', 'wb')
pickle.dump(my_list, pickle_file)
pickle_file.close()
def diaoyong():
pickle_file = open('my_list.pkl', 'rb')
my_list2 = pickle.load(pickle_file)
print(my_list2)
if __name__ == '__main__':
#dabao()
diaoyong()
🔰异常处理(OLD:P33-34)
P33~34 你不可能总是对的
python标准异常
表 python常见标准异常
异常名称 | 描述 | 处理 | |
---|---|---|---|
AssertionError | 断言语句(assert)失败 | ||
AttributeError | 尝试访问未知的对象属性 | ||
EOFError | 用户输入文件末尾标志EOF(Ctrl+d) | ||
FileNotFoundError | 找不到文件 | try: sun = 1 + ‘1’ f1 = open(“testfile”, “w”) print(f1.read) f1.close() except OSError as reason: print(‘文件出错!\n’ + str(reason)) except TypeError as reason: print(‘类型出错!\n’ + str(reason)) | |
FloatingPointError | 浮点计算错误 | ||
GeneratorExit | generator.close()方法被调用的时候 | ||
ImportError | 导入模块失败的时候 | ||
IndexError | 索引超出序列的范围 | ||
KeyError | 字典中查找一个不存在的关键字 | ||
KeyboardInterrupt | 用户输入中断键(Ctrl+c) | ||
MemoryError | 内存溢出(可通过删除对象释放内存) | ||
NameError | 尝试访问一个不存在的变量 | ||
NotImplementedError | 尚未实现的方法 | ||
OSError | 操作系统产生的异常(例如打开一个不存在的文件) | ||
OverflowError | 数值运算超出最大限制 | ||
ReferenceError | 弱引用(weak reference)试图访问一个已经被垃圾回收机制回收了的对象 | ||
RuntimeError | 一般的运行时错误 | ||
StopIteration | 迭代器没有更多的值 | ||
SyntaxError | Python的语法错误 | ||
IndentationError | 缩进错误 | ||
TabError | Tab和空格混合使用 | ||
SystemError | Python编译器系统错误 | ||
SystemExit | Python编译器进程被关闭 | ||
TypeError | 不同类型间的无效操作 | ||
UnboundLocalError | 访问一个未初始化的本地变量(NameError的子类) | ||
UnicodeError | Unicode相关的错误(ValueError的子类) | ||
UnicodeEncodeError | Unicode编码时的错误(UnicodeError的子类) | ||
UnicodeDecodeError | Unicode解码时的错误(UnicodeError的子类) | ||
UnicodeTranslateError | Unicode转换时的错误(UnicodeError的子类) | ||
ValueError | 传入无效的参数 | ||
ZeroDivisionError | 除数为零 |
异常层次结构
- BaseException
- SystemExit
- KeyboardInterrupt
- GeneratorExit
- Exception
- StopIteration
- ArithmeticError
- FloatingPointError
- OverflowError
- ZeroDivisionError
- AssertionError
- AttributeError
- BufferError
- EOFError
- ImportError
- LookupError
- IndexError
- KeyError
- MemoryError
- NameError
- UnboundLocalError
- OSError
- BlockingIOError
- ChildProcessError
- ConnectionError
- BrokenPipeError
- ConnectionAbortedError
- ConnectionRefusedError
- ConnectionResetError
- FileExistsError
- FileNotFoundError
- InterruptedError
- IsADirectoryError
- NotADirectoryError
- PermissionError
- ProcessLookupError
- TimeoutError
- ReferenceError
- RuntimeError
- NotImplementedError
- SyntaxError
- IndentationError
- TabError
- IndentationError
- SystemError
- TypeError
- ValueError
- UnicodeError
- UnicodeDecodeError
- UnicodeEncodeError
- UnicodeTranslateError
- UnicodeError
- Warning
- DeprecationWarning
- PendingDeprecationWarning
- RuntimeWarning
- SyntaxWarning
- UserWarning
- FutureWarning
- ImportWarning
- UnicodeWarning
- BytesWarning
- ResourceWarning
#### 异常检测与处理
-
try-except语句
- 检测到异常后,不会运行接下来的程序了
- 检测到异常后,不会运行接下来的程序了
-
try-finally语句
- raise语句
- 自己引发异常
- raise testerror(‘此为异常解释内容’)
🔰else 和 with(OLD:P35)
P35丰富的else语句及简洁的with语句
else语句用法扩展
while配合else:
如果break,不会进行语句2;如果全部执行完循环,不执行语句2.
while 表达式1:
if 表达式2:
语句1
break
语句2
else:
语句3
try结合else:
如果try下的内容出错,正常执行exception;否则执行else
try:
语句1
except ValueError as reason:
语句2
else:
语句3
with语句
-
with语句仅仅能对支持上下文管理协议的对象使用。支持本协议的对象
- file
- decimal.Context
- thread.LockType
- threading.Lock
- threading.RLock
- threading.Condition
- threading.Semaphore
- threading.BoundedSemaphore -
语法结构
with context_expr() as var:
doSomething()
-
解析
- context_expr()为上下文表达式,先执行,获取上下文对象后调用_enter_()方法
- 无论语句是否正常结束,都会调用_exit_()方法
P36 图形用户界面EasyGui
easygui官网
easygui学习文档(中文版)
三种调用方式
import easygui
easygui.msgbox('hi,pig')
from easygui import *
msgbox('hi,pig')
import easygui as g
g.msgbox('hi,pig')
🔰类和对象(OLD:P37~P41)
相关名词解释
- 类(class):用来描述具有相同的属性和方法的对象的集合,类的内部定义了每个对象共有的属性和方法。
- 属性:静态的特征
- 方法:动态的动作,类中定义的函数
- 对象:通过类定义的数据结构实例。对象=属性+方法,对象包括两个数据成员(类变量和实例变量)和方法。
- 实例化:创建一个类的实例,类的具体对象。
- 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
介绍
- 类和对象是一种封装
- python中约定类名以大写字幕开头
- 面向对象:OO,Object Oriented
OO的特征:
- 封装:将数据、方法封装在class中。只需知道名字进行调用即可,保密性更高
- 可参考:OO封装、继承、多态
- 继承:子类能够自动获取父类的数据和方法
- 多态:同一方法可根据发送对象的不同而采用不同的响应
- 一个对象的实际类型是确定的,但是指向对象的引用类型有很多
- 一个对象的实际类型是确定的,但是指向对象的引用类型有很多
类的一个创建和调用、继承、多态的例子:
# 对象的属性、方法调用举例
class Cooldream:
"""关于类的一个简单例子"""
# 属性
interesting = '计算机编程、手工DIY'
gender = '保密'
telephone = '181********'
# 方法
def learnPython(self):
print('先看完看完小甲鱼的教程...')
def learnEnglish(self):
print('先背单词吧...')
# 继承举例,继承list
class Myclass(Cooldream):
pass
# 多态举例
class A:
def fun(self):
print('这里是A...')
class B:
def fun(self):
print('这里是B...')
if __name__ == '__main__':
print('----对象的属性、方法调用举例----')
# 调用对象的属性
test = Cooldream()
print('gender:', test.gender)
# 调用对象中的方法
test = Cooldream()
test.learnPython()
# 继承举例
print('----继承举例,它可以使用现有Cooldream类的所有功能----')
class1 = Myclass()
print('继承来的gender:', class1.gender)
print('继承来的learnPython:👇👇')
class1.learnPython()
# 多态举例
print('----多态举例,调用的方法都是fun,但响应不同----')
a = A()
b = B()
输出结果:
----对象的属性、方法调用举例----
gender: 保密
先看完看完小甲鱼的教程...
----继承举例,它可以使用现有Cooldream类的所有功能----
继承来的gender: 保密
继承来的learnPython👇👇
先看完看完小甲鱼的教程...
----多态举例,调用的方法都是fun,但响应不同----
这里是A...
这里是B...
相关名词解释:
- 类(class):类=属性+方法,用来描述具有相同的属性和方法的对象的集合,类的内部了每个对象共有的属性和方法。
- 属性:静态的特征
- 方法:动态的动作,类中的函数
- 对象:通过类的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
- 实例化:创建一个类的实例,类的具体对象。
OOA:面向对象分享
OOD:面向对象设计
OOP:面向对象编程
self的用法
self 代表类的实例,self 在类的方法时是必须有的,要将self写进第一个参数,虽然在调用时不必传入相应的参数。
例一
# self使用举例1
class Ball1:
def setName(self, name):
self.name = name
def kick(self):
print('我叫%s, 哎呀!谁踢我...' % self.name)
if __name__ == '__main__':
# self使用举例1
print('----self使用举例1----')
a = Ball1()
a.setName('AA')
b = Ball1()
b.setName('BB')
c = Ball1()
c.setName('TT')
a.kick()
c.kick()
###运行结果
----self使用举例----
我叫AA, 哎呀,谁踢我...
我叫TT, 哎呀,谁踢我...
例二、推荐方法,更简化
# self使用举例2
class Ball2:
def __init__(self, name):
self.name = name
def kick(self):
print('我叫%s, 哎呀!谁踢我...' % self.name)
if __name__ == '__main__':
# self使用举例2
print('----self使用举例2----')
a = Ball2('AA')
b = Ball2('BB')
c = Ball2('TT')
a.kick()
c.kick()
###运行结果
----self使用举例----
我叫AA, 哎呀,谁踢我...
我叫TT, 哎呀,谁踢我...
- init(self,param1, param2, …)方法称为构造方法
- 实例化一个对象时,此方法将会在对象创建时自动调用
- 可以在实例化对象时传入参数,这些参数讲自动传入_init__方法中
- 可通过重写_init__方法,来自初始化操作
私有变量或私有函数
- 在变量名或函数名前加上
__
, - (访问方式一) 私有变量需通过内部函数访问,无法使用
p.属性1
进行访问,如示例中的“访问方式一” - (访问方式二) 但“私有”其实是“伪私有”,可通过
p._类名__属性名
进行外部访问,如示例中的“访问方式二”
示例
# 公有变量,即普通变量
class TestG:
attribute = 'attribute变量的值'
# 私有变量
class TestS:
__attribute = '__attribute变量的值'
def getAttribute(self):
return self.__attribute
if __name__ == '__main__':
# 访问公有变量
print('----访问公有变量----')
p = TestG()
print(p.attribute)
# 访问私有变量
print('----访问私有变量----')
p = TestS()
print('访问方式一:', p.getAttribute())
print('访问方式二:', p._TestS__attribute)
继承
语法:class DrivedClassNName(BaseClassName):
- DrivedClassNName是子类
- BaseClassName是父类(基类、超类)
- 子类可继承父类的所有属性和方法
例子见上👆
- 如果子类中父类同名的方法或属性,则会自动覆盖父类对应的方法或属性
- 如果子类中的方法覆盖了父类中的方法,需要先引入 父类中的方法,有两种解决办法
- 调用未绑定的父类方法
父类名.父类中被覆盖的方法名(self)
- super函数(更好的方案)
super().父类中被覆盖的方法名()
- 不用传入self参数
- 优点是,修改继承的父类时,不用修改引入代码,
super()
会自动查找
- 调用未绑定的父类方法
- 如果子类中的方法覆盖了父类中的方法,需要先引入 父类中的方法,有两种解决办法
多重继承
- 语法:
class DrivedClassNName(Base1, Base2, Base3, ...):
# 多重继承
class Base1:
def t1(self):
print('我是t1')
class Base2:
def t2(self):
print('我是t2')
class Base3:
def t3(self):
print('我是t3')
class C(Base1, Base2, Base3):
pass
if __name__ == '__main__':
c = C()
c.t1()
c.t2()
c.t3()
- 缺点:
- 容易造成代码混乱,非必须就不用
拾遗
组合
- 把类的实例化放到新类中,就把旧类组合一起了。
- 优点:不需要多重继承,没有造成代码混乱的风险
示例
# 组合举例
class Turtle:
def __init__(self, x):
self.num = x
class Fish:
def __init__(self, x):
self.num = x
class Pool:
def __init__(self, x, y):
self.turtle = Turtle(x)
self.fish = Fish(y)
def print_num(self):
print('水池里一共有乌龟 %d 只, 小鱼 %d 条' % (self.turtle.num, self.fish.num))
if __name__ == '__main__':
pool = Pool(1, 10)
pool.print_num()
类、类对象和实例对象
对实例对象的属性进行赋值时,将生成一个新的实例对象属性,去覆盖类对象的属性
示例
# 对实例对象的属性进行赋值时,将生成一个新的实例对象属性,去覆盖类对象的属性,示例
class C1:
count = 0
if __name__ == '__main__':
a = C1()
b = C1()
c = C1()
print('----原始 a, b, c 的count属性:----\n', a.count, b.count, c.count)
c.count += 10
print('----把c 的实例化对象count属性+10 | a, b, c 的count属性:----\n', a.count, b.count, c.count)
print('----类对象 C1 的count属性:\n', C1.count)
C1.count += 100
print('----把类对象 C1 的count属性+100 | a, b, c 的count属性:----\n', a.count, b.count, c.count)
----原始 a, b, c 的count属性:----
0 0 0
----把c 的实例化对象count属性+10 | a, b, c 的count属性:----
0 0 10
----类对象 C1 的count属性: 0
----把类对象 C1 的count属性+100 | a, b, c 的count属性:----
100 100 10
-
类中的属性时静态的,且类属性和类对象是相互绑定的,不会受实例对象属性的影响
-
示例对象在对属性操作时,会创建新的实例对象属性,操作实例对象属性时,不影响类属性
-
注意:
- 如果属性名与方法名相同,属性会覆盖方法,如下示例
# 如果属性名与方法名相同,属性会覆盖方法,如下示例
class C:
def x(self):
print('X-man')
if __name__ == '__main__':
c = C()
print('---可调用方法 .x() ---')
c.x()
print('---增加属性名x,方法 .x() 被覆盖---')
c.x = 1 # 创建一个实例对象c的一个属性,由于python变量不需声明,因此直接赋值就相当于定义了一个x属性
print('c.x =', c.x)
c.x()
print('.x()被覆盖,不能调用了...')
建议:
- 如果属性和方法多而复杂,可使用 继承 和 组合 的方法扩展类
- 属性名用名词
- 方法名用动词
绑定
实例对象通过self与class中的方法绑定。没绑定的话,实例对象就不能调用class方法,举个没绑定的例子:
示例一、没有self
# 缺少self,将无法与实例对象绑定
class BB:
def printBB(): # 缺少self,将无法与实例对象绑定
print('no zuo no die')
if __name__ == '__main__':
# 缺少self,将无法与实例对象绑定
print('----类对象调用:----')
BB.printBB()
print('----实例对象调用:----')
bb = BB()
bb.printBB()
示例二、有self
# 有self,可绑定
class CC:
def setXY(self, x, y):
self.x = x
self.y = y
def printXY(self):
print(self.x, self.y)
if __name__ == '__main__':
# 有self,可绑定
print('----------------------------------')
dd = CC()
print('dd属性>>', dd.__dict__)
print('CC属性>>', CC.__dict__, '\n')
print('----传入参数----')
dd.setXY(4, 5)
dd.printXY()
print('dd属性>>', dd.__dict__)
print('CC属性>>', CC.__dict__, '\n')
print('----删除类CC,实例对象dd依然可调用CC中的方法,并且设置实例属性dd.x、dd.y ----')
del CC
dd.printXY()
dd.setXY(5, 6)
dd.printXY()
注:
通过 dd.dict 可查看对象属性,以字典形式返回,加上print才能在pycharm中打印出结果,IDE中无需加print
在这里,self的一个作用是,将实例对象传入class方法,将他们绑定
缺少self,就无法绑定,实例对象调用方法时将会报错
dd.setXY(4, 5)
相当于dd.setXY(dd, 4, 5)
, 默认将实例对象dd
传入进方法,形成dd.x
与dd.y
类和对象相关的BIF
- issubclass(class, classinfo) 判断一个类是否是指定类的子类
- 如果第一个参数(class)是第二个参数(classinfo)的一个子类,则返回True,否则返回False
- 一个类会被认为是其自身的子类
- classinfo可以是一个元组,都多个class构成,然后依次检索每个候选的类是否时它的子类
- 如下示例
# issubclas判断一个类是否是指定类的子类,示例
class A:
pass
class B(A):
pass
class C:
pass
if __name__ == '__main__':
print('B 是 A 的子类?', issubclass(B, A), '\n')
print('B 是 B 的子类?', issubclass(B, B), '\n')
print('B 是 object 的子类?', issubclass(B, object), '\n') # object是所有类的基类
print('B 是 C 的子类?', issubclass(B, C), '\n')
- isinstance(object, classinfo) 判断一个实例对象是否是指定类的实例对象
- 如果第一个参数(object)是第二个参数(classinfo)的实例对象,则返回True,否则返回False
- 如果第一个参数不是对象,则返回False;如果第二个参数不是类或由类组成的元组,则会抛异常TyptError
- classinfo可以是一个元组,都多个class构成,然后依次检索每个候选的类是否时它的子类
- 如下示例
# isinstance判断一个实例对象是否是指定类的实例对象,示例
class A:
pass
class B(A):
pass
class C:
pass
if __name__ == '__main__':
# issubclas判断是否是子类,示例
print('-----------------issubclass判断是否是类的实例对象-----------------')
print('B 是 A 的子类?', issubclass(B, A), '\n')
print('B 是 B 的子类?', issubclass(B, B), '\n')
print('B 是 object 的子类?', issubclass(B, object), '\n') # object是所有类的基类
print('B 是 C 的子类?', issubclass(B, C), '\n')
# isinstance判断一个实例对象是否是指定类的实例对象,示例
print('-----------------isinstance判断是否是类的实例对象-----------------')
b1 = B()
print('b1 是 B 的实例对象?', isinstance(b1, B), '\n')
print('b1 是 C 的实例对象?', isinstance(b1, C), '\n')
print('b1 是 A 的实例对象?', isinstance(b1, A), '\n')
print('b1 是 (A, B, C)的实例对象?', isinstance(b1, (A, B, C)), '\n')
- hasattr(object, name) 判断一个对象里是否有指定的属性
- attr = attribute: 属性
- object是对象,name是属性名(字符串类型,要用引号)
# hasattr 判断一个对象里是否有指定的属性,示例
class D:
def __init__(self, x=0):
self.x = x
if __name__ == '__main__':
# hasattr 判断一个对象里是否有指定的属性,示例
print('-----------------hasattr 判断一个对象里是否有指定的属性-----------------')
d1 = D()
hasattr(d1, 'x')
print('d1 有 x 属性?', hasattr(d1, 'x'), '\n')
- getattr(object, name[, default]) 返回对象指定的属性值
- 如果指定的属性不存在,则返回default(可选参数);若没有设置default参数,则抛异常AttributeError
- setattr(object, name, value) 设置对象中指定属性的值
- 如果指定的属性不存在,则会新建属性并赋值
- delattr(object, name) 删除对象中指定的属性
- 如果属性不存在,抛出异常AttributeError
# getattr 返回对象指定的属性值,示例
# setattr 设置对象中指定属性的值,示例
# delattr 删除对象中指定的属性,示例
class D:
def __init__(self, x=0):
self.x = x
if __name__ == '__main__':
# getattr 返回对象指定的属性值
print('-----------------getattr 返回对象指定的属性值-----------------')
d1 = D()
print('d1 有 x 属性是:', getattr(d1, 'x'), '\n')
print('d1 有 x 属性是:', getattr(d1, 'y', '您所访问的对象不存在!'), '\n')
print('d1 有 x 属性是:', getattr(d1, 'y'), '\n')
# setattr 设置对象中指定属性的值
print('-----------------setattr 设置对象中指定属性的值-----------------')
print('d1 有 y 属性是:', getattr(d1, 'y', '您所访问的对象不存在!'), '\n')
print('设置 d1 中 y 的属性:FishC')
setattr(d1, 'y', 'FishC')
print('d1 有 y 属性是:', getattr(d1, 'y'), '\n')
# delattr 删除对象中指定的属性
print('----------------- delattr 删除对象中指定的属性-----------------')
delattr(d1, 'y')
delattr(d1, 'y') # 将会报错 AttributeError
- property(fget=None, fset=None, fdel=None, doc=None) 用来通过属性操作属性
- fget 是获取属性的方法名
- fset 是设置属性的方法名
- fdel 是删除属性的方法名
- 可参考:property 的详细使用方法
- 优点:
- 用户只需调用x来间接获取修改后的一些(fget, fsee, fdel) 属性。
- 不然,如果将class中的方法名全部改了,用户也要跟着改很多代码
# property 通过属性操作属性
class C:
def __init__(self, size=10):
self.size = size
def getSize(self):
return self.size
def setSize(self, value):
self.size = value
def delSize(self):
del self.size
x = property(getSize, setSize, delSize)
if __name__ == '__main__':
# property 通过属性操作属性
print('----------- property 通过属性操作属性----------- ')
c = C()
# print('c.getSize() =', c.getSize())
print('通过c.x 自动调用getSize() | c.x =', c.x) # 调用getSize()
c.x = 18 # 调用SetSize()
print('通过c.x=18 自动调用 SetSize() | c.x =', c.x)
print('c.size =', c.size)
del c.x # 调用DelSize()
print('通过del c.x 自动调用 DelSize() | ', c.size) # 此时,size属性已被删除,将报错AttributeError: 'C' object has no attribute 'size'
🔰魔法方法(OLD:P42~P49)
- 总是被双下划线包围,如__init__
- 很强,体现在在适当的时候自动被调用
- 参考:
- 单下划线、双下划线、头尾双下划线说明:
- foo: 的是特殊方法,一般是系统名字 ,类似 init() 之类的。
- _foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import *
- __foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了。
- 单下划线、双下划线、头尾双下划线说明:
P42 构造和析构
方法 | 描述 & 简单的调用 |
---|---|
init ( self [,args…] ) | 构造函数 class中的__init__ - 在需要传入新参数(默认只传入self),重__init__(self)为__init__(self, x, y) - 不能进行return,会报错 - |
简单的调用方法: obj = className(args) |
| new(cls[, …]) |
- 实例化对象时,第一个被调用的魔法方法
- 它的第一个参数是类,其他的参数会直接传递给__init__()方法
- 需要继承一个不可变类型,又需要进行修改时特性时,就用到了__new__重
|
| del( self ) |
- 析构方法, 删除一个对象
- 它是一个垃圾回收机制
- 所有对象对class的引用都被del之后,参会触发。如示例
简单的调用方法 : del obj
|
| repr( self ) | 转化为供解释器读取的形式
简单的调用方法 : repr(obj) |
| str( self ) | 用于将值转化为适于人阅读的形式
简单的调用方法 : str(obj) |
| cmp ( self, x ) | 对象比较
简单的调用方法 : cmp(obj, x) |
# __new__(cls[, ...]) 示例
class CapStr(str): # 定义一个继承于 str 的大写字符串
def __new__(cls, string):
string = string.upper()
return str.__new__(cls, string)
'''
这里是要定义一个继承str特性的类CapStr
但是CapStr又要把输入的字符串变成大写
str没有这个性质,它是不能改变的
所以要进行修改__new__方法
新建类CapStr,在它那里用str的方法变大写
把转换得到的结果作为输入的字符串
再传给str的方法__new__,就实现了需求
类CapStr就成了继承str且能把输入变大写的类
'''
if __name__ == '__main__':
# __new__(cls[, ...]) 示例
a = CapStr('a b cd efg')
print('\n__new__重写后,Capstr(str)既继承了str,又可将字符串变大写:', a)
# __del__( self ) 示例
class C:
def __init__(self):
print('我是__init__(),我被调用了...')
def __del__(self):
print('我是__del__(),我被调用了...')
if __name__ == '__main__':
# __del__( self ) 示例
c1 = C()
c2 = c1 # c2引用c1
c3 = c2 # c3引用c2
print('\n>>>>现在开始依次del引用 C() 类的实例对象\n')
del c3
del c2
del c1
print('\n>>>>看到了吧,直到所有被引用的c1、c2、c3都被del了之后,__del__()才会被自动调用')
P43 算术运算1~
- 关于工厂函数:
- python2.2以前,类和类型是分开的。类是属性和方法的封装,类型是整型、字符型、字符串这种
- python2.2之后,类和类型进行了统一 ,诸如将int()、float()、str()、list()、tuple()等这些内置函数(BIF)转换为工厂函数(类对象)
# 工厂函数于BIF, 示例
class C:
pass
if __name__ == '__main__':
# 工厂函数于BIF, 示例
print('type(len): ', type(len), '\n') # 普通的BIF,返回<class 'builtin_function_or_method'> ,他是一个内置的函数或方法
print('type(dir): ', type(dir), '\n') # 普通的BIF,
print('type(int): ', type(int), '\n') # 工厂函数,返回<class 'type'> ,
print('type(list): ', type(list), '\n') # 工厂函数,当调用它们的时候,其实就是创建了一个相应的实例对象
print('type(C): ', type(C), '\n')
a = int('123') # 实例化一个int对象,返回一个实例后的对象,传入的参数是123(a是int的实例对象)
b = int('459')
print('a+b: ', a + b, '\n') # python对两个对象相加
-
算数运算符
| 方法 | *描述( 定义 ) |
| — | — |
| add(self, other) | 加法:+ |
| sub(self, other) | 减法:- |
| mul(self, other) | 乘法: |
| truediv(self, other) | 真除法:/ ,真除法就是有小数的 |
| floordiv(self, other) | 整数除法:// ,所谓的地板除 |
| mod(self, other) | 取模算法:% |
| divmod(self, other) | 当被 divmod() 调用时 ,就是地板除的余数 |
| pow(self, other[, modulo]) | 当被 power() 调用或 ** 运算时 |
| lshift(self, other) | 按位左移位:<< |
| rshift(self, other) | 按位右移位:>> |
| and(self, other) | 按位与操作:& |
| xor(self, other) | 按位异或操作:^ |
| or(self, other) | 按位或操作:| | -
add(self, other)、sub(self, other) ,示例1
# __add__(self, other)、__sub__(self, other) ,示例1
class New_int(int):
def __add__(self, other):
return int.__sub__(self, other)
def __sub__(self, other):
return int.__add__(self, other)
if __name__ == '__main__':
# __add__(self, other)、__sub__(self, other) ,示例1
a = New_int(3)
b = New_int(5)
print('a+b:', a + b, '\n') # 加号触发了 __add__(self,other) 方法
print('a-b:', a - b, '\n') # 减号触发了 __sub__(self,other) 方法
# __add__(self, other)、__sub__(self, other) ,示例2
class New_int(int):
def __add__(self, other):
return (int(self) + int(other)) # 将self与other强制转换为整型,所以不会出现两个对象相加不断触发__add__()的情况
def __sub__(self, other):
return (int(self) - int(other))
if __name__ == '__main__':
# __add__(self, other)、__sub__(self, other) ,示例2
a = New_int(3)
b = New_int(5)
print('a + b =', a + b)
'''
# 无限递归的情况
# __add__(self, other)、__sub__(self, other) ,示例2
class New_int(int):
def __add__(self, other):
return (self + other)
def __sub__(self, other):
return (self + other)
if __name__ == '__main__':
# __add__(self, other)、__sub__(self, other) ,示例2
a = New_int(3)
b = New_int(5)
print('a + b =', a + b)
'''
P44 算数运算2~
- 当一个对象进行相关算数运算操作时,自动触发对应的魔法方法。一旦重写了魔法方法,就能按照重写的方法运行。增加了更多的灵活性。
- 如:重写int后,当我们进行加法运算时,给出一个减法运算结果, 示例如下
# 重写int:当我们进行加法运算时,给出一个减法运算结果,示例
class int(int): # 当类int变成类对象,就能将原来的工厂函数int覆盖掉
def __add__(self, other):
return int.__sub__(self, other)
if __name__ == '__main__':
# 重写int:当我们进行加法运算时,给出一个减法运算结果,示例
a = int('5')
print("int('5') =", a)
b = int(3)
print('int(3) =', b)
print('a + b =', a + b)
反运算
与算术运算符一一对应
** 方法** | 描述 |
---|---|
radd(self, other) | 当左操作数不支持相应的操作时被调用 如:a + b,a不支持 加法运算时,他就会自动实现b的加法反运算 再如:1 + b 如下示例1 注意: - 重写反运算要注意参数的顺序问题 - 1 + b 的时候,return int.sub(self, other)中的self指的时 b,other指的是前面的 1 - 如果要进行正常加减,要将self与other互换位置 |
如下示例2 | |
rsub(self, other) | |
rmul(self, other) | |
rtruediv(self, other) | |
rfloordiv(self, other) | |
rmod(self, other) | |
rdivmod(self, other) | |
rpow(self, other) | |
rlshift(self, other) | |
rrshift(self, other) | |
rand(self, other) | |
rxor(self, other) | |
ror(self, other) |
示例1,示例2
# 反运算,示例1
class Nint(int):
def __radd__(self, other):
return int.__sub__(self, other)
if __name__ == '__main__':
print('----------反运算,示例1-----------')
a = Nint(5)
b = Nint(3)
print('a + b =', a + b)
print('1 + b =', 1 + b) # 由于 1 没有__add__()方法,所以b的__radd__()被 1 自动触发执行
# 注意:1 + b 的时候,return int.__sub__(self, other)中的sel指的时 b,other指的是前面的 1(输出:3 - 1 = 2)
# 反运算,示例2
class Nint2(int):
def __rsub__(self, other):
return int.__sub__(other, self) # 将self与other互换位置才能正常进行减法
if __name__ == '__main__':
print('----------反运算,示例2-----------')
c = Nint2(5)
print('3 - c =', 3 - c) # 由于3无__add__()方法,所以执行b的反运算__radd__(self,other)方法,将c传入了self
其他运算操作
增量赋值运算
使用时,与重写普通魔法方法一样
方法 | 描述( 赋值 ** 的行为 ) |
---|---|
iadd(self, other) | 加法:+= |
isub(self, other) | 减法:-= |
imul(self, other) | 乘法:*= |
itruediv(self, other) | 真除法:/= |
ifloordiv(self, other) | 整数除法://= |
imod(self, other) | 取模算法:%= |
ipow(self, other[, modulo]) | 幂运算:**= |
ilshift(self, other) | 按位左移位:<<= |
irshift(self, other) | 按位右移位:>>= |
iand(self, other) | 按位与操作:&= |
ixor(self, other) | 按位异或操作:^= |
ior(self, other) | 按位或操作:|= |
一元操作符
方法 | 描述( 定义** 的行为 ) |
---|---|
pos(self) | 正号:+x |
neg(self) | 负号:-x |
abs(self) | 当被 abs() 调用时 |
invert(self) | 按位求反:~x |
类型转换
方法 | 描述( 定义 ** 的行为 ) |
---|---|
complex(self) | 当被 complex() 调用时(需要返回恰当的值) |
int(self) | 当被 int() 调用时(需要返回恰当的值) |
float(self) | 当被 float() 调用时(需要返回恰当的值) |
round(self[, n]) | 当被 round() 调用时(需要返回恰当的值) |
index(self) | 1. 当对象是被应用在切片表达式中时,实现整形强制转换 2. 如果你定义了一个可能在切片时用到的定制的数值型,你应该定义 index 3. 如果 index 被定义,则 int 也需要被定义,且返回相同的值 |
上下文管理(with 语句)
方法 | 描述( 定义 ** 的行为 ) |
---|---|
enter(self) | 1. 定义当使用 with 语句时的初始化行为 2. enter 的返回值被 with 语句的目标或者 as 后的名字绑定 |
exit(self, exc_type, exc_value, traceback) | 1. 定义当一个代码块被执行或者终止后上下文管理器应该做什么 2. 一般被用来处理异常,清除工作或者做一些代码块执行完毕之后的日常工作 |
P45 简单定制(实战任务)
实战任务:简单定制一个计时器
基本要求:
- 定制一个计时器的类
- start和stop方法代表启动计时 和 停止计时
- 假设计时器对象t1,print(t1)和直接调用t1均显示结果
- 当计时器未启动或已经停止计时,调用stop方法会给予温馨的提示
- 两个计时器对象可以进行相加:t1 + t2
- 只能使用提供的有限资源完成
需要使用的资源:
- 使用time模块的localtime方法获取时间
- 扩展阅读:time 模块详解(时间获取和转换)
time.localtime([secs])
接收时间辍(1970 纪元年后经过的浮点秒数)并返回当地时间下的时间元组 t(t.tm_isdst 可取 0 或 1,取决于当地当时是不是夏令时)- time.localtime返回struct_time的时间格式
- 以时间元祖(struct_time)的形式返回
- 0~5个元素就是从年到秒
- 表现你的类:str 和 repr
- 重写他们,如示例1
重写 str 和 repr,示例1
# 重写 __str__ 和 __repr__,示例1
class A:
def __str__(self):
return '__str__() 被重写了,所以调用 print 时打印出了我!'
class B:
def __repr__(self):
return '__repr__() 被重写了,所以运行 实例对象b 时打印出了我...'
if __name__ == '__main__':
a = A()
print(a)
a # 直接运行a,不会有输出。但重写__repr__() 后,就可以有输出了
b = B()
print(b)
b # 需要在IDLE上运行才有输出,才能看到两者的区别:a返回的是<__main__.A object at 0x00000000032683D0>,而b返回的是__repr__() 被重写了,所以运行 实例对象b 时打印出了我...
实战任务:简单定制一个计时器,示例2
import time as t # 导入 time 模块的 t
import time
class Mytimer():
# 在 __init__ 中初始化 定义一些变量
def __init__(self):
self.unit = ['年', '月', '天', '小时', '分钟', '秒']
self.prompt = "未开始计时"
self.lasted = []
self.begin = 0
self.end = 0
def __str__(self):
return self.prompt
__repr__ = __str__ # 将 __str__ 的方法复制给 __repr__
def __add__(self, other): # 重写加法操作符,运行时间相加
prompt = "它们总共运行了:"
result = []
for index in range(6):
result.append(self.lasted[index] + other.lasted[index])
if result[index]:
prompt += (str(result[index]) + self.unit[index])
return prompt
# 开始计时
def start(self):
self.begin = t.localtime()
self.prompt = ("提示:请先调用stop()停止计时!")
print('计时开始...')
# 停止计时
def stop(self):
if not self.begin:
print('提示:请先调用start()进行计时!')
else:
self.end = t.localtime()
self._calc()
print('计时结束!')
# 内部方法,计算运行时间
def _calc(self):
self.prompt = "总共运行了:"
for index in range(6):
self.lasted.append(self.end[index] - self.begin[index])
if self.lasted[index]: # 判断是否为零,非零才往下走
self.prompt += (str(self.lasted[index]) + self.unit[index]) # 时间 + 单位
# 为下一轮计时初始化变量
self.begin = 0
self.end = 0
if __name__ == '__main__':
print('------------开始使用 t1 计时------------')
t1 = Mytimer()
print('<<<未调用start,未调用stop>>>,将有如下提示:')
print(t1)
print('<<<未调用start,就调用stop>>>,将有如下提示:')
t1.stop() # 提示:请先调用start() 进行计时!
print('<<<先调用start,后调用stop>>>,将有如下正常提示:')
t1.start() # 计时开始...
time.sleep(6)
t1.stop() # 计时结束!
print(t1) # 总共运行了 秒
print('------------开始使用 t2 重新计时------------')
t2 = Mytimer()
t2.start() # 计时开始...
t2.stop() # 计时结束!
print(t2) # 总共运行了 秒
print('------------t1 和 t2 合计运行------------')
print(t1 + t2) # 总共运行了 秒
'''
# 教程里的原程序,可在IDLE运行
t1 = Mytimer()
t1
t1.stop() # 提示:请先调用start() 进行计时!
t1.start() # 计时开始...
t1.stop() # 计时结束!
t1 # 总共运行了 秒
t2 = Mytimer()
t2.start() # 计时开始...
t2.stop() # 计时结束!
t2 # 总共运行了 秒
t1 + t2 # 总共运行了 秒
'''
P46 属性访问
四种访问方式
- 直接访问:
对象.属性
- getatter()访问:
getatter(对象, '属性名', '没有此属性!')
- property 通过属性操作属性:示例1
- 魔法方法的重写
# 前三种方法访问属性,示例1
class C:
def __init__(self, size=10):
self.size = size
self.y = 'Y-man'
def getSize(self):
return self.size
def setSize(self, value):
self.size = value
def delSize(self):
del self.size
x = property(getSize, setSize, delSize)
if __name__ == '__main__':
c = C()
print('--------------------方法一:')
print(c.y)
print('--------------------方法二:')
print(getattr(c, 'y', '没有此属性!'))
print('--------------------方法三:property 通过属性操作属性')
print(c.x)
'''
# IDEL版本
c = C()
c.y
getattr(c, 'y', '没有此属性!')
c.x
'''
通过魔法方法的重写获取属性
注意啦,魔法方法会被自动触发调用
方法 | 描述( 定义 ** 的行为 ) |
---|---|
getattr(self, name) | 当用户试图获取一个不存在的属性时 |
getattribute(self, name) | 当该类的属性被访问时 |
setattr(self, name, value) | 当一个属性被设置时 |
delattr(self, name) | 当一个属性被删除时 |
看这几个魔法方法被调用的顺序和触发点,示例2
# 看这几个魔法方法被调用的顺序和触发点,示例2
class C:
def __getattribute__(self, name):
print('getattribute')
# 使用 super() 调用 object 基类的 __getattribute__ 方法
return super().__getattribute__(name)
def __setattr__(self, name, value):
print('setattr')
super().__setattr__(name, value)
def __delattr__(self, name):
print('delattr')
super().__delattr__(name)
def __getattr__(self, name):
print('getattr')
if __name__ == '__main__':
c = C()
print('-------------------运行 c.x-------')
c.x
print('-------------------运行 c.x = 1-------')
c.x = 1
print('-------------------运行 c.x-------')
c.x
print('-------------------运行 del c.x-------')
del c.x
'''
# IDEL版本
c = C()
c.x
c.x = 1
c.x
del c.x
'''
小练习
练习要求
- 写一个矩形类,默认有宽和高两个属性;
- 如果为一个叫square的属性赋值,那么说明这是一个正方形,值就是正方形的边长,此时宽和高都应该等于边长
# 小练习,示例2
class Rectangle:
def __init__(self, width=0, height=0):
self.width = width
self.height = height
def __setattr__(self, name, value):#一发生赋值操作,则会触发__setattr__()魔法方法
if name == 'square':#判断name属性是否为正方形
self.width = value
self.height = value
else:
# self.name = value # __init__()中的赋值操作自动触发__setattr__(),self.name = value又进行了赋值操作,又自动触发__setattr__()。如此无线递归循环
# super().__setattr__(name, value) # 调用基类的__setattr__(),如果直接使用self.name = value,会形成无限递归
self.__dict__[name] = value # 代替上一行程序,__dict__[]以字典的形式显示当前对象的所有属性及其对应的值
# super().__setattr__()这种调用基类的魔法方法的方式比较通用
def getArea(self):
return self.width * self.height
if __name__ == '__main__':
r1 = Rectangle(4, 5)
r1.getArea()
r1.square = 10
r1.width
r1.height
r1.getArea()
'''
# IDEL版本
r1 = Rectangle(4,5)
r1.getArea()
r1.square = 10
r1.width
r1.height
r1.getArea()
'''
P47 描述符
通过此节可弄清楚描述符,可以研究property的实现原理
- 描述符就是将某种特殊类型的类的实例指派给另一个类的属性。
- 所谓“特殊类型”,至少要满足 实现以下方法的其中一个
__get__(self, instance, owner)
– 用于访问属性时会被自动调用,它返回属性的值- self(描述符类本身的一个实例),
- instance(描述符拥有者的类的实例),
- owner(描述符拥有者的类)
__set__(self, instance, value)
– 将在属性分配操作中会被自动调用,不返回任何内容__delete__(self, instance)
– 控制删除操作会被自动调用,不返回任何内容
描述符示例,示例1
# 描述符示例,示例1
class MyDecriptor:
# 有以下三个方法,满足描述符中的“特殊类型”
def __get__(self, instance, owner):
print("getting...", self, instance, owner)
def __set__(self, instance, value):
print("setting...", self, instance, value)
def __delete__(self, instance):
print("deleting...", self, instance)
class Test:
x = MyDecriptor() # 取 MyDecriptor 类的实例指派给Test类的属性x,称MyDecriptor()就是 x 的描述符,MyDecriptor就是描述符类
if __name__ == '__main__':
test = Test()
test.x # 自动调用描述符的 __get__(),输出结果的三个参数分别为: self(描述符类本身的一个实例), instance(描述符拥有者的类Test的实例test), owner(描述符拥有者的类Test)
print(test)
print(Test)
test.x = "X-man" # 自动调用描述符的 __set__(),value参数(test.x = "X-man" 中 的 "X-man")
del test.x # 自动调用描述符的__delete__()
'''
# IDEL版
test = Test()
test.x
test
Test
test.x = "X-man"
del test.x
'''
定义一个属于自己 的property——MyProperty,实现property所有功能。示例2
property的几个必须的参数;
- self
- fget=None
- fset=None
- fdel=None
# 定义一个属于自己 的property——MyProperty,实现property所有功能。示例2
class MyProperty:
def __init__(self, fget=None, fset=None, fdel=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
def __get__(self, instance, owner):
return self.fget(instance)
def __set__(self, instance, value):
self.fset(instance, value)
def __delete__(self, instance):
self.fdel(instance)
class C:
def __init__(self):
self._x = None
def getX(self):
return self._x
def setX(self, value):
self._x = value
def delX(self):
del self._x
x = MyProperty(getX, setX, delX)
if __name__ == '__main__':
c = C()
c.x = "XXXXXX"
print(c.x)
print(c._x)
del c.x
'''
# IDEL版
c = C()
c.x = "HELLOW"
c.x
c._x
del c.x
'''
小练习:
练习要求
- 先定义一个温度类,然后定义两个描述符类用于描述摄氏度和华氏度两个属性。
- 要求两个属性会自动进行转换,也就是说你可以给摄氏度这个属性赋值,然后打印的华氏度属性是自动转换后的结果。
- 华氏度与摄氏度转换公式:摄氏度*1.8+32 = 华氏度
小练习,描述摄氏度和华氏度两个属性及自动转换,示例3
# 小练习,描述摄氏度和华氏度两个属性及自动转换,示例3
class Celsius: # 摄氏度描述符类
def __init__(self, value=26.0): # self为描述符类自身(此为摄氏度描述符类)的实例(此为cel)
self.value = float(value) # 转换成浮点数运算
def __get__(self, instance, owner): # instance是这个描述符的拥有者所在的类的实例(此为temp)
return self.value # 当class Temperature: 中的cel属性被获得时,返回self.value
def __set__(self, instance, value): # owner是这个描述符的拥有者所在的类本身(此为温度类)
self.value = float(value) # 当class Temperature: 中的cel属性被设置时,进行赋值操作
class Fahrenheit: # 华氏度描述符类
def __get__(self, instance, owner):
return instance.cel * 1.8 + 32 # 摄氏度转华氏度
def __set__(self, instance, value):
instance.cel = ((float)(value) - 32) / 1.8 # 华氏度转摄氏度
class Temperature: # 温度类
cel = Celsius() # 设置摄氏度属性(描述符类的实例指派给了温度类的属性)
fah = Fahrenheit() # 设置华氏度属性
if __name__ == '__main__':
temp = Temperature()
print(temp.cel)
temp.cel = 30
print(temp.fah)
temp.fah = 100
print(temp.cel)
'''
# IDEL版
temp = Temperature()
temp.cel
temp.cel = 30
temp.fah
temp.fah = 100
temp.cel
'''
P48 定制序列(定制容器)
协议
- 协议(Protocols)与其他编程语言中的接口很相似,它规定你哪些方法必须要定义。
- 而在Python中的协议就显得不那么正式,在Python中的协议更像是一种指南。
容器
- 容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用in, not in关键字判断元素是否包含在容器中。
- 容器是一种可以包含其他类型对象(如列表、元组、字典等)作为元素的对象。
容器类型的协议
- 如果说你希望定制的容器是不可变的话,你只需要定义__len__()和__getitem__()方法。
- 如果你希望定制的容器是可变的话,除了__len__()和__getitem__()方法,你还需要定义__setitem__()和__delitem__()两个方法。
容器类型的魔法方法
方法 | 描述( 定义 ** 的行为 ) |
---|---|
len(self) | 当被 len() 调用时(返回容器中元素的个数) |
getitem(self, key) | 获取容器中指定元素,相当于 self[key] |
setitem(self, key, value) | 设置容器中指定元素,相当于 self[key] = value |
delitem(self, key) | 定义删除容器中指定元素,相当于 del self[key] |
iter(self) | 当迭代容器中的元素 |
reversed(self) | 当被 reversed() 调用时 |
contains(self, item) | 当使用成员测试运算符(in 或 not in)时 |
# 定义记录列表中每个元素访问次数类,示例
class CountList: # 定义记录列表中每个元素访问次数类
def __init__(self, *args): # 参数是可变类型的
self.values = [x for x in args] # 将args的数据存入列表中,通过列表推导式存放到values列表
self.count = {}.fromkeys(range(len(self.values)),0) # 创建字典,初试化为0(列表下标对应字典中的键,值就对应键值)
def __len__(self): # 返回容器中元素的个数
return len(self.values)
def __getitem__(self, key): # 获取容器中指定元素的行为
self.count[key] += 1 # 每访问一次,字典键对应的键值加1
return self.values[key]
if __name__ == '__main__':
c1 = CountList(1, 3, 5, 7, 9)
c2 = CountList(2, 4, 6, 8, 10)
print(c1[1]) # c1[1]第一次访问
print(c2[2])
c1[1] + c2[2] # c1[1]第二次访问
print(c1.count)
print(c2.count)
'''
# IDEL版
c1 = CountList(1,3,5,7,9)
c2 = CountList(2,4,6,8,10)
c1[1] #c1[1]第一次访问
c2[2]
c1[1] + c2[2] #c1[1]第二次访问
c1.count
c2.count
'''
迭代器
提供迭代方法的容器就是迭代器
通常的迭代器有:序列、字典,如: 使用for循环对序列进行迭代,每次从容器中拿取一个数据
迭代类似循环,每次重复的过程就是一次迭代;而一次迭代的结果往往作为下次迭代的初始值
序列、字典的迭代,示例1
# 序列、字典的迭代,示例1
for i in 'abcdefg':
print(i)
test = {'a':'A', 'b':'B','c':'C' ,'d':'D' ,'e':'E'}
for each in test:
print("%s -> %s" %(each, test[each]))
python提供了两个迭代器BIF:
- inter()
- 一个容器对象调用inter()就得到了迭代器
- next()
- 调用next(),迭代器就会返回下一个值,若没有下个值,就抛异常
inter()、next(),示例2
#inter()、next(),示例2
string = "FishC"
it = iter(string)
print(next(it))
print(next(it))
print(next(it))
print(next(it))
print(next(it))
# next(it) # 运行将抛异常
'''IDLE版
string = "FishC"
it = iter(string)
next(it)
next(it)
next(it)
next(it)
next(it)
'''
for语句工作原理,用while语句实现for,示例3
# for语句工作原理,用while语句实现for,示例3
string = "FishC"
it = iter(string)
while True:
try:
each = next(it)
except StopIteration:
break
print(each)
迭代器的魔法方法
iter()、next()分别是inter()、next()对应的魔法方法
- 一个容器如果是迭代器,那就必须实现__iter__(),iter()能返回迭代器本身
return self
- next()决定了迭代器规则
迭代器实现可控斐波那契数列,示例4、5
# 迭代器实现斐波那契数列,示例4
class Fibs:
def __init__(self):
self.a = 0
self.b = 1
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b, self.a + self.b
return self.a
fibs = Fibs()
for each in fibs:
if each < 20:
print(each)
else:
break
'''
# 迭代器实现斐波那契数列,可控制迭代范围,示例5
class Fibs:
def __init__(self, n=10):
self.a = 0
self.b = 1
self.n = n
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b, self.a + self.b
if self.a > self.n:
raise StopIteration
return self.a
fibs = Fibs()
for each in fibs:
print(each)
print('------------------------')
fibs = Fibs(100)
for each in fibs:
print(each)
'''
🔰生成器(OLD:P50)
P50乱入,生成器
- 无需类和方法对象,仅需要普通函数即可实现,相比迭代器,生成器使得ython更为简洁
- 生成器需要在普通函数里有 yield 语句
- 生成器使得协同程序可以实现
- 可参考: 提高你的 Python:解释 yield 和 Generators(生成器)—— 鱼C工作室
协同程序(生成器的应用)
所谓协同程序,就是可以运行的独立函数调用,函数可以暂停或者挂起,并在需要的时候从程序离开的地方继续或者重新开始。
yield相当于普通函数中的return语句,它在返回数据的同时,能将函数执行过程暂停于此
用生成器实现的几个例子,示例1~2
# 用生成器实现的几个例子,示例1~
# 生成器运行过程示例,示例1
def myGen():
print("生成器被执行了!")
yield 1
yield 2
yield 3
myG = myGen()
next(myG)
next(myG)
for i in myGen():
print(i)
print('-------------------------------')
# 斐波那契也可以用生成器来实现,示例2
def fibs():
a = 0
b = 1
while True: # 生成器随时都会暂停,不用担心死循环
a, b = b, a + b
yield a
for each in fibs():
if each > 100:
break
print(each, end=' ')
四种推导式(包含了生成器推导式)
# 列表推导式
a = [i for i in range(100) if not (i % 2) and (i % 3 )] # 100以内,能被2整除,但不能被3整除的所有整数
print(a)
print('-------------------------------')
# 字典推导式:
a = {i:i % 2 == 0 for i in range(10)} # 10以内是否为偶数
print(a)
print('-------------------------------')
# 集合推导式:
a = {i for i in [1, 2, 3, 3, 3, 6, 5, 5, 6, 7, 7, 8]}
print(a)
print('-------------------------------')
# (元组)生成器推导式:
e = (i for i in range(5))
print(next(e))
print(next(e))
print(next(e))
for each in e:
print(each, end=' ')
print('\n-------------------------------')
# 生成器推导式可作为函数的参数:把sum里面的内容是生成器推导式,把生成器推导式里生成的数据都加起来,计算100以内不能被2整除的数的和
print(sum(i for i in range(100) if i % 2))
🔰模块 (OLD:P51~P53)
P53 模块就是程序
介绍
容器是对数据的封装
函数是对语句的封装
类是对方法和属性的封装
模块就是对程序的封装,一个python文件
命名空间
模块.方法
,如: hello.hi()
导入与调用模块
- 方法一、
import 模块名
- 调用:
模块.模块中的函数名(参数)
- 调用:
- 方法二、
from 模块名 import 函数名
,函数名可以使用*代替,用于导入模块中的所有命名空间- 调用:
模块中的函数名(参数)
- 缺点:可能出现模块中的函数名与程序中的函数名重复的情况
- 调用:
方法三、import 模块名 as 新模块名
推荐!- 调用:
新模块名.模块中的函数名(参数)
- 优势:重命名模块名,可使长的模块名缩短
- 调用:
示例 - 模块
# TemperatureConversion.py
def c2f(cal):
fah = cal * 1.8 + 32
return fah
def f2c(fah):
cal = (fah - 32)/1.8
return cal
示例 - 调用
# 方法一
import TemperatureConversion
print("32 摄氏度 = %.2f 华氏度" % TemperatureConversion.c2f(32))
print("99 华氏度 = %.2f 摄氏度" % TemperatureConversion.f2c(99))
# 方法二
from TemperatureConversion import c2f, f2c # 或from TemperatureConversion import *
print("32 摄氏度 = %.2f 华氏度" % c2f(32))
print("99 华氏度 = %.2f 摄氏度" % f2c(99))
# 方法三
import TemperatureConversion as tc
print("32 摄氏度 = %.2f 华氏度" % tc.c2f(32))
print("99 华氏度 = %.2f 摄氏度" % tc.f2c(99))
P52 name == ‘main’、搜索路径和包
模块的好处:
- 实现代码的重复利用:先封装成模块,需要使用时,导入模块,再调用函数即可
if name == 'main’
注意:
- 模块文件在前期编写、调试的时候,要添加测试程序,已测试模块中的各个方法能否正常运行,如示例1
# 示例1
def c2f(cal ):
return cal * 1.8 + 32
def f2c(fah):
return (fah - 32)/1.8
def test():
print("测试:", "0摄氏度 = %.2f 华氏度\n" % c2f(0))
print("测试:", "0华氏度 = %.2f 摄氏度" % f2c(0))
test()
__name__变量如何使用?
- 如果在主程序中运行
__name__
,会返回__main__ - 如果在主程序中使用模块调用
__name__
,如tc.__name__
,会返回模块名
if name = _ ‘main’_:可以方便避免在主程序调用模块的时候,运行到模块中的test()程序
- 把
if ``__name__ == _ ``_'main'_:_
test()
加入主程序,这就是个程序运行入口 - 把
if ``__name__ == _ ``_'main'_:_
test()
加入模块文件,那么它里面的test()就不会运行,因为不满足if的条件
搜索路径
- 查看搜索路径:
import sys
sys.path
- 最佳存放模块的路径:site-packages 文件夹
- 添加搜索路径
sys.path.append('你的模块目录路径')
,如sys.path.append("C:\\Python39\\test")
包(package)
用于解决模块太多而杂乱无章、并且可能出现命名冲突的问题
创建
- 创建一个文件夹,用于存放模块,文件夹的名字即包的名字;
- 在文件夹中创建一个__init__.py的模块文件,内容可以为空,用于告诉python要将这个文件夹当成一个包;
- 将相关的模块放入文件夹中。
调用import 包名.模块名
然后,参考模块的导入与调用即可,同样的方法,有三种方法
P53 像一个极客去思考
使用现成的模块,如:python标准库中的模块
python标准库中的模块数百个,怎么自己去探索模块?如下:
查看pyhon自带文档:IDLE -> Help ->Python Docs F1
目录中的英文 | 包含的内容 |
---|---|
What’s New in Python | python新的特性 |
The Python Tutorial | 简易的教程 介绍了语法 |
Installing Python Modules | 安装python的第三方模块 |
Distributing Python Modules | 发布第三方模块 pypi社区,全世界分享的模块:https://pypi.org/ |
The Python Language Reference | python语法、设计哲学 |
Python Setup and Usage | 在不同平台使用 |
Python HOWTOs | 详细深入探讨主题 |
Extending and Embedding the Python Interpreter | 使用C/C++开发python扩展模块 |
Python/C API Reference Manual | 扩展模块API函数 |
PEP | python增强建议书 用来规范python开发 PEP0: PEP索引:https://www.python.org/dev/peps/ |
文档搜索:
在文档点索引,或搜索,如搜索timeit
其中:
第一段时介绍模块
Basic Examples:基础例程
Examples:详细运用的例子
快速掌握模块的用法
- 先导入模块,然后调用__doc__属性,查看模块简介。如
import timeit
print(timeit.__doc__)
- 使用dir()可以查询到该模块定义了哪些变量、函数和类。如
dir(timeit)
,- 其中的__all__属性:
timeit.__all__
可以给我们提供可供外界调用的东西 - 其中的__file__属性:
timeit.__file__
可以给我们提供模块源代码所在位置- 阅读源代码可以快速提升能力哦!
- 其中的__all__属性:
- 使用
help(timeit)
帮助文档,比print(timeit.__doc__)
调出来的简单一点
- 注意:
- 不是所有模块都有__all__属性
- 如果模块有__all__属性,那么使用*导入全部命名空间的话 如
from timeit import *
,只有timeit.__all__
显示出来的才能被导入 - 因此,推荐在编写模块文件时,将所有对外提供的接口和类,都设置到__all__属性的列表中
- IT英语:
- IT英语版块 ——FishC
- 每天进步一点点,做最好的自己[专辑] ——FishC:破渔网兜兜
- 最后 timeit模块太有用了,建议阅读源码等
🔰爬虫
P54 论一只爬虫的自我修养
python如何访问互联网?
-
url + lib = urllib
- url就是网址,lib就是网址首页
- urllib是一个包,包含了4个模块:
urllib.request
for opening and reading URLsurllib.error
containing the exceptions raised by urllib.requesturllib.parse
for parsing URLsurllib.robotparser
for parsing robots.txt files
-
url的一般格式
- protocol 😕/ hostname[:port] / path / [;parameters][?query]#fragment
-
URL由三部分组成:
- 协议:http,https,ftp,file,ed2k…
- 存放资源的服务器的域名系统或IP地址(有时候要包含端口号,各种传输协议都有默认的端口号,如http的默认端口为80)。
- 资源的具体地址,如目录或文件名等。
-
urllib中访问网页的函数
urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
- url是必须的,其他都是有默认参数或是可选参数
示例
import urllib.request
response = urllib.request.urlopen('http://www.fishc.com')
html = response.read() # 读取网页
print(html)
print('-------------------------------------------------------------')
html = html.decode('utf-8') # 网页二进制解码
print(html)
P55 论2:实战
程序注释都写了
爬取下载一只猫,示例1
一只猫的网站url:http://placekitten.com/g/500/600
# 爬取下载一只猫的示例
import urllib.request
response = urllib.request.urlopen('http://placekitten.com/g/500/600') # 括号里面可以是url地址
cat_imag = response.read()
with open('cat_500_600.jpg', 'wb') as f: # 图片也是二进制格式的,用'wb'参数控制写入文件
f.write(cat_imag)
'''
import urllib.request
# 也可以这样写,将上面的response = urllib.request.urlopen()拆成下面的两行
req = urllib.request.Request('http://placekitten.com/g/500/600')# 获取request对象
response = urllib.request.urlopen(req) # 括号里面也可以是request对象
cat_imag = response.read()
with open('cat_500_600.jpg', 'wb') as f: # 图片也是二进制格式的,用'wb'参数控制写入文件
f.write(cat_imag)
'''
'''
print(response.geturl(), '\n\n') # 得到访问的链接地址
response.info() # 得到httpmessage对象,是远程服务器返回来的信息,包含了header信息
print(response.info(), '\n\n')
print(response.getcode(), '\n\n') # 得到http的状态码,200表示ok,就是正常响应
'''
有道词典翻译
知识点
- 网页请求方式(客户端向服务器请求):
- get 从服务器请求获得数据
- post 向指定服务器提交被处理的数据
- Headers——request headers
- 客户端请求头
- User-Agent:包含了客户端信息,能用于在服务器端判断是非为人或爬虫程序访问
- Headers——Form Data
- 提交的主要内容
- [技术交流] Python编码问题的解决方案总结
有道词典翻译,可参考:用Python破解有道翻译反爬虫机制https://zhuanlan.zhihu.com/p/47785984
'''
思路是:
输入翻译内容
打开检查 -- network
点击翻译按钮,或按回车
看到network下有很多GET类型的请求和一个Post类型的请求
那么Post请求就是要研究的目标
点开Post请求对应的路径:进入headers,有很多重要信息,包括:
request url
data
输出html后发现得到一段json数据,字符串json的形式,包含了翻译结果。json里面包含了字典。
import json, 处理json数据,通过json.loads()载入字符串,得到字典
通过关键词访问字典中键对应的值,获得一个两层列表
'''
# 此程序和教程中稍有不同,因为相比2014年,现在的有道翻译做了调整(表单结构data)。按照教程中的思路总结如上
import urllib.request
import urllib.parse
import json
content = input('请输入需要翻译的内容:')
url = 'http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule'
data = {}
data['i'] = '啊啊啊啊'
data['from'] = 'AUTO'
data['to'] = 'AUTO'
data['smartresult'] = 'dict'
data['client'] = 'fanyideskweb'
data['salt'] = '16285587902727'
data['sign'] = '296a1061a0ba4763f7cd7e22d5790840'
data['lts'] = '1628558790272'
data['bv'] = 'eda468fc64295ecf2810ab8a672c2db1'
data['doctype'] = 'json'
data['version'] = '2.1'
data['keyfrom'] = 'fanyi.web'
data['action'] = 'FY_BY_REALTlME'
data = urllib.parse.urlencode(data).encode('utf-8') # 把编码后的data值覆盖data
response = urllib.request.urlopen(url, data)
html = response.read().decode('utf-8') # read()之后得到的是utf-8编码文件
'''
decode()是把其他编码形式变成Unicode的形式
encode()是把Unicode的形式变成其他形式
'''
# print(html) # 输出的是json格式,接下来还要处理提取 ↓
# json.loads(html)
target = json.loads(html)
# type(target) # 观察target类型,是字典类型
# target['translateResult']
# target['translateResult'][0][0]
print('翻译结果为 %s:' % (target['translateResult'][0][0]['tgt']))
P56 论3:隐藏
服务器一般通过headers中的UA检查 是什么东西在访问
两种方式添加/修改headers:
- 设置一个headers,作为参数传给request
- 在request生成之后,通过调用.add_header()把它加进去
有道词典翻译,示例1
可参考,助于理解:用Python破解有道翻译反爬虫机制https://zhuanlan.zhihu.com/p/47785984
'''
# 有道词典翻译,示例1
基于上一个程序,增加隐藏手段
思路是:
增加headers中的UA
'''
import urllib.request
import urllib.parse
import json
content = input('请输入需要翻译的内容:')
url = 'http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule'
'''# 设置一个headers,作为参数传给request
head = {}
head['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36'
'''
data = {}
data['i'] = content
data['from'] = 'AUTO'
data['to'] = 'AUTO'
data['smartresult'] = 'dict'
data['client'] = 'fanyideskweb'
data['salt'] = '16285587902727'
data['sign'] = '296a1061a0ba4763f7cd7e22d5790840'
data['lts'] = '1628558790272'
data['bv'] = 'eda468fc64295ecf2810ab8a672c2db1'
data['doctype'] = 'json'
data['version'] = '2.1'
data['keyfrom'] = 'fanyi.web'
data['action'] = 'FY_BY_REALTlME'
data = urllib.parse.urlencode(data).encode('utf-8') # 把编码后的data值覆盖data
req = urllib.request.Request(url, data)
req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36')
response = urllib.request.urlopen(url, data)
html = response.read().decode('utf-8') # read()之后得到的是utf-8编码文件
'''
decode()是把其他编码形式变成Unicode的形式
encode()是把Unicode的形式变成其他形式
'''
# print(html) # 输出的是json格式,接下来还要处理提取 ↓
# json.loads(html)
target = json.loads(html)
# type(target) # 观察target类型,是字典类型
# target['translateResult']
# target['translateResult'][0][0]
print('翻译结果为: %s' % (target['translateResult'][0][0]['tgt']))
还有个一问题,服务器记录你的访问速度,如果你的IP一秒访问很多次,那一定是爬虫,就被识别到了
两个方法
- 方法一、延迟每次访问时间
- 方法二、使用代理ip
- 步骤:
-
- 参数是一个字典 {‘类型’:‘代理ip:端口号’}
proxy_support = urllib.request.ProxyHandler({})
-
- 定制、创建一个 opener
- opene用于打开网页,可以给它加上特殊的headers、指定相关的代理等
opener = urllib.request.build_opener(proxy_support)
-
- 安装 opener
- 安装后,以后调用方便
urllib.request.install_opener(opener)
-
- 调用 opener
- 可以可以不安装
opener.open(url)
-
- 步骤:
方法一,示例2
'''
# 有道词典翻译,示例2
基于上一个程序,增加:自动执行下一次、延迟、增加退出程序提示
思路是:
在外层增加while循环,用于自动执行下一次
纯粹增加等待时间time.sleep(3)
使用if判断输入内容是否为指定内容,而让程序退出
'''
import urllib.request
import urllib.parse
import json
import time
while True:
content = input('请输入需要翻译的内容:')
if content == 'q!':
break
url = 'http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule'
'''# 设置一个headers,作为参数传给request
head = {}
head['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36'
'''
data = {}
data['i'] = content
data['from'] = 'AUTO'
data['to'] = 'AUTO'
data['smartresult'] = 'dict'
data['client'] = 'fanyideskweb'
data['salt'] = '16285587902727'
data['sign'] = '296a1061a0ba4763f7cd7e22d5790840'
data['lts'] = '1628558790272'
data['bv'] = 'eda468fc64295ecf2810ab8a672c2db1'
data['doctype'] = 'json'
data['version'] = '2.1'
data['keyfrom'] = 'fanyi.web'
data['action'] = 'FY_BY_REALTlME'
data = urllib.parse.urlencode(data).encode('utf-8') # 把编码后的data值覆盖data
req = urllib.request.Request(url, data)
req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36')
response = urllib.request.urlopen(url, data)
html = response.read().decode('utf-8') # read()之后得到的是utf-8编码文件
'''
decode()是把其他编码形式变成Unicode的形式
encode()是把Unicode的形式变成其他形式
'''
# print(html) # 输出的是json格式,接下来还要处理提取 ↓
# json.loads(html)
target = json.loads(html)
# type(target) # 观察target类型,是字典类型
# target['translateResult']
# target['translateResult'][0][0]
target = target['translateResult'][0][0]['tgt']
print('翻译结果为: %s' % target)
time.sleep(3)
方法二,通过代理访问,示例3
'''
# 通过代理访问,示例3
免费代理网址
http://www.kxdaili.com/dailiip/2/1.html
https://jahttp.zhimaruanjian.com/getapi/ _2021/8/10 可用,不报错
出错有可能是代理IP的问题
'''
import urllib.request
import random
# url = 'http://www.whatismyip.com.tw/' # 可测试目前ip
url = 'http://www.ip111.cn/'
iplist = ['183.162.159.227:4245', '115.239.102.56:4267', '59.58.104.78:4245', '221.231.75.90:4232', '114.239.172.153:4245']
proxy_support = urllib.request.ProxyHandler({'http': random.choice(iplist)})
opener = urllib.request.build_opener(proxy_support)
opener.addheaders = [('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36')]
urllib.request.install_opener(opener)
response = urllib.request.urlopen(url)
html = response.read().decode('utf-8')
print(html)
P57 论4:OOXX
教程源码,含必要注释:
'''
目前2021/8/10,程序从网站爬取图片出现404错误,即使增加了header中的所有内容,也暂时无法解决
网站增加了base编码:
编码与解码参考:python中base64编码与解码:https://blog.csdn.net/fireflylane/article/details/84674509
类似网站:
• https://www.mzitu.com/245908/1
• https://www.mm618.com/albums/4701/1
'''
import urllib.request
import os
import random
def url_open(url):
req = urllib.request.Request(url)
req.add_header(':authority', 'jandan.net')
req.add_header(':method', 'GET')
req.add_header(':path', '/ooxx')
req.add_header(':scheme', 'https')
req.add_header('accept',
'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9')
req.add_header('accept-encoding', 'gzip,deflate,br')
req.add_header('accept-language', 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7')
req.add_header('cache-control', 'max-age=0')
req.add_header('cookie',
'BAIDU_SSP_lcr=https://www.baidu.com/link?url=Ezh6mU9WWWexbF_wRh-yJ2ETgTgeIbv_TK5znIZoTpG&wd=&eqid=cee1c8350007d23f00000003611246f2;_ga=GA1.2.469504217.1628587767;_gid=GA1.2.3149650.1628587768;__gads=ID=b0a6d594108c21ef-2240c337bdca00e8:T=1628587768:RT=1628587768:S=ALNI_MZWPHOJA7J3PtkE2I4fl7nirml7dg;PHPSESSID=ubbl5s6g2rqh14fvcao6lonl1b')
req.add_header('sec-ch-ua', '"Chromium";v="92","NotA;Brand";v="99","GoogleChrome";v="92"')
req.add_header('sec-ch-ua-mobile', '?0')
req.add_header('sec-fetch-dest', 'document')
req.add_header('sec-fetch-mode', 'navigate')
req.add_header('sec-fetch-site', 'none')
req.add_header('sec-fetch-user', '?1')
req.add_header('User-Agent',
'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/92.0.4515.131Safari/537.36')
# -----------------------可选择使用代理---------------------
iplist = ['183.162.159.227:4245', '115.239.102.56:4267', '59.58.104.78:4245', '221.231.75.90:4232', '114.239.172.153:4245']
proxy = random.choice(iplist)
proxy_support = urllib.request.ProxyHandler({'http': proxy})
opener = urllib.request.build_opener(proxy_support)
urllib.request.install_opener(opener)
# -----------------------可选择使用代理---------------------'''
response = urllib.request.urlopen(url)
html = response.read()
print(html)
return html
def get_page(url):
html = url_open(url).decode('utf-8')
a = html.find('current-comment-page ') + 23
b = html.find(']', a)
print('html[a:b] = ', html[a:b])
return html[a:b]
def find_imgs(url): # 获取每页中 图片的地址,保存为列表
html = url_open(url).decode('utf-8')
img_addrs = []
a = html.find('img src=')
# 一个页面中有多个图片,因此循环搜索
while a != -1:
b = html.find('.jpg', a , a+255)
if b != -1:
img_addrs.append(html[a+9:b+4])
else:
b = a + 9
a = html.find('img src=', b)
# for each in img_addrs:
# print(each)
return img_addrs
def save_imgs(folder, img_addrs): # 保存图片函数
for each in img_addrs:
filename = each.split('/')[-1]
with open(filename, 'wb') as f:
img = url_open(each)
f.write(img)
def download_mm(folder='OOXX', pages=10): # 主函数
os.mkdir(folder) # 创建文件夹
os.chdir(folder) # 切换工作目录
url = 'http://jiandan.net/ooxx/'
page_num = int(get_page(url)) # 将返回的字符串变为整型
for i in range(pages): # 循环获取前十页
page_num -= i
page_url = url +'page-' + str(page_num) + '#comments'
img_addrs = find_imgs(page_url)
save_imgs(folder, img_addrs)
if __name__ == '__main__':
download_mm()
技巧总结:
- 编写主函数,分析所需子函数:
- 搭建好子函数框架
- 编写子函数
- 调试运行
P58 论5:正则表达式
小甲鱼教程:[扩展阅读] Python3 如何优雅地使用正则表达式(详解一)
工具网站:
https://regex101.com/,练习正则表达式,实时显示标记匹配结果
常用的通配符
- ,如.docx --> 查找docx后缀的文件
- ?
python爬虫需求:
按照需要的内容特征自动去网页里查找
而,正则表达式本身就是用于描述那些复杂特征的
正则表达式
python通过re模块实现正则表达式
- re.search()方法
- 扫描整个字符串,并返回第一个成功的匹配。如果匹配失败,则返回None。
- 参考:https://www.runoob.com/python/python-reg-expressions.html
re.search(pattern, string, flags=0)
- pattern : 正则中的模式字符串,也就是搜索规则。
- string : 要被查找/替换的原始字符串。
- flags : 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。
正则表达式特殊符号
**.**
,通配符- 一个英文的句号,他能代表换行符以外的任何字符
\
,反斜杠- 能使元字符(如 . )失去它的特殊能力
- 如果要消除表达式的某个功能,就在表达式前加上反斜杠。如示例中的
re.search(r'\.', 'it is fishc.com')
\d
,匹配任何数字[]
,字符类- 为了表示一个字符串的范围,可创建一个字符类
- 只要匹配字符类中的任一个字符,都算匹配
- 不区分大小写:
[ - ]
表示一个范围- 如
[a-z]
[0-9]
- 如
{}
,限定重复匹配的次数
示例
import re
# 用于搜索指定字符串位置,正则和find()方法都行
test1_1 = re.search(r'fishc', 'it is fishc.com')
test1_2 = 'it is fishc.com'.find('fishc')
print('test1-------', test1_1, test1_2, '\n')
# . 它能代表换行符以外的任何字符
test2_1 = re.search(r'.', 'it is fishc.com')
test2_2 = re.search(r'fishc.', 'it is fishc.com')
print('test2-------', test2_1, test2_2, '\n')
# 如果想要单单匹配一个点
test3_1 = re.search(r'\.', 'it is fishc.com')
print('test3-------', test3_1, '\n')
# \d
test4_1 = re.search(r'\d', 'aaa123 it is fishc.com')
test4_2 = re.search(r'\d\d\d', 'aaa123 it is fishc.com')
print('test4-------', test4_1, test4_2, '\n')
# 匹配一个ip地址
test5_1 = re.search(r'\d\d\d\.\d\d\d\.\d\d\d', 'cba 192.168.000.100 abc')
test5_2 = re.search(r'(([01]{0,1}\d{0,1}\d|2[0-4]\d|25[0-5])\.){3}([01]{0,1}\d{0,1}\d|2[0-4]\d|25[0-5])', 'cba 192.168.0.1 abc')
print('test5-------', test5_1, test5_2, '\n')
# []
test6_1 = re.search(r'[aeiou]', 'it is fishc.com')
test6_2 = re.search(r'[a-e]', 'it is fishc.com')
print('test6-------', test6_1, test6_2, '\n')
# {}
test7_1 = re.search(r'ab{3}c', 'abbbcddefffg')
test7_2 = re.search(r'ab{3,10}c', 'abbbbbbcddefffg') # b可匹配3~10个皆可
print('test7-------', test7_1, test7_2, '\n')
# 表示一个0-255
test8_1 = re.search(r'[01]\d\d|2[0-4]\d|25[0-5]', '188')
print('test8-------', test8_1, '\n')
P59 论6:正则表达式2
[扩展阅读] Python3 正则表达式特殊符号及用法(详细列表)——小甲鱼 付费
Python——正则表达式特殊符号及用法 —— 免费 参考
特殊符号会用就行,用到去查
特殊符号示例
import re
# |
test1_1 = re.search(r'fish(c|d)', 'it is fishc.com')
test1_2 = re.search(r'fish(c|d)', 'it is fishd.com')
print('test1-------', test1_1, test1_2, '\n')
# ^ 脱字符 指定字符必须出现在被搜索字符串开头
test2_1 = re.search(r'^fish(c|d)', 'it is fishc.com')
test2_2 = re.search(r'^fish(c|d)', 'fishc.com')
print('test2-------', test2_1, test2_2, '\n')
# $ 匹配结尾位置
test3_1 = re.search(r'fish(c|d)$', 'fishc.com')
test3_2 = re.search(r'fish(c|d)$', 'it is fishc')
print('test3-------', test3_1, test3_2, '\n')
# \ ()
test4_1 = re.search(r'(fishc)\1', 'it is fishc.com') # (fishc)\1 相当于fishcfishc ,镜像复制
test4_2 = re.search(r'(fishc)\1', 'it is fishcfishc')
test4_3 = re.search(r'(fishc)\060', 'it is fishcfishc0')
test4_4 = re.search(r'(fishc)\141', 'it is fishcfishca')
print('test4-------', test4_1, test4_2, test4_3, test4_4, '\n')
# ^ [] findall能返回列表
test5_1 = re.search(r'[.]', 'cba 192.168.000.100 abc') # 表示一个点
test5_2 = re.findall(r'[\n]', 'cba 192.168.000.100 abc\n') # 此时表示转义符
test5_3 = re.findall(r'[^a-z]', 'cba 192.168.000.100 abc\n') # ^放在前面表示取反,除了a-z 其他的都搜
print('test5-------', test5_1, test5_2, test5_3, '\n')
# {M,N}
test6_1 = re.search(r'ab{3}c', 'abbbcddefffg') # 匹配b3此
test6_2 = re.search(r'(ab){3}c', 'abababccd') # 匹配ab三次
test6_3 = re.search(r'(ab){1,3}c', 'abababccd') # 匹配ab1~3次
print('test6-------', test6_1, test6_2, test6_3, '\n')
# 贪婪模式 与 非贪婪模式
s = '<html><title>fishc.com</title></html>'
test7_1 = re.search('r<.+>', s)
test7_2 = re.search('r<.+?>', s)
print('test7-------', test7_1, test7_2, '\n')
P60 论7:正则表达式3
Python——正则表达式特殊符号及用法 —— 免费 参考
特殊符号会用就行,用到去查
但是你要知道有这个方法,所以要先看一遍都有啥
- 编译正则表达式
- 如果需要重复使用某个正则表达式,就可以线将该正则表达式编译成模式对象
- re.compile()
import re
# 编译正则表达式
p = re.compile(r"[A-Z]")
test1_1 = p.search('it is FishC.com')
test1_2 = p.findall('it is FishC.com')
print('-------------test1', test1_1, test1_2, '\n')
-
编译标志
- 通过编译标志,可以修改正则表达式的工作方式
- 可同时使用多个编译标志,中间用 | ,如re.I | re.M
- [扩展阅读] Python3 如何优雅地使用正则表达式(详解三)
-
开启详细正则表达式模式
- 此模式支持空格和tab缩进和注释,能让表达式看上去更直观、易读
- re.VERBOSE
P61 论8:正则表达式4
- srearch()方法
- 有两种调用方法:
- 模块形式调用:re.search(pattern, string, flag=0)
- 参数
- pattern参数:模式
- string参数:要搜索的字符串
- flag参数:编译标志位
- 返回结果的捕获
- .group(),如下示例1
- result = re.search(f" (\w+) (\w+)", ‘it is fishc.com’)
- print(result.group())
- .group()可以设置参数
- .group(1),参数代表子组索引。没有参数默认提取所有内容
- .group(),如下示例1
- 返回匹配的起始位置及范围
- .start()
- .end()
- .span() 范围
- 参数
- 编译后的正则表达式模式对象:regex = re.compile(),regex.secrch(string[, pos[, endpos]])
- string[参数:要搜索的字符串
- pos[可选参数:要搜索的起始位置
- endpos]]可选参数:要搜索的结束位置
- 示例:rx.rearch(string, 0, 50) 等价于 rx.rearch(string[:50], 0)
- 模块形式调用:re.search(pattern, string, flag=0)
- 有两种调用方法:
- findall()方法
- 功能
- 返回匹配到的内容
- 调用
- re.findall(pattern, string, flags=0)
- 如果正则表达式包含子组,将会把子组内容单独返回;如果存在多个子组,将会将匹配内容组合成元组再返回
- 如示例中的p = r’<img class=“BDE_Image” src="([^"]+.jpg)’ imglist = re.findall(p, html)
- 其中的src="([^"]+.jpg中的括号就是子组
- 如示例中的p = r’<img class=“BDE_Image” src="([^"]+.jpg)’ imglist = re.findall(p, html)
- 如何不捕获子组?
- (?:…) 非捕获组,即该子组匹配的字符串无法从后面获取
- 功能
下载贴吧中的图片,示例1
# 下载贴吧中的图片https://tieba.baidu.com/p/7473449745
import re
import urllib.request
#
def open_url(url):
req = urllib.request.Request(url)
req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36')
page = urllib.request.urlopen(req)
html = page.read().decode('utf-8')
return html
def get_img(html):
p = r'<img class="BDE_Image" src="([^"]+\.jpg)'
imglist = re.findall(p, html)
for each in imglist:
print(each)
filename = each.split("/")[-1]
urllib.request.urlretrieve(each, filename, None)
if __name__ == '__main__':
url = "https://tieba.baidu.com/p/7473449745"
get_img(open_url(url))
爬取免费代理ip清单,示例2
# 爬取免费代理ip清单 https://ip.jiangxianli.com/
import re
import urllib.request
def open_url(url):
req = urllib.request.Request(url)
req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36')
page = urllib.request.urlopen(req)
html = page.read().decode('utf-8')
return html
def get_img(html):
p = r'(?:(?:[0,1]?\d?\d|2[0-4]\d|25[0-5])\.){3}(?:[0,1]?\d?\d|2[0-4]\d|25[0-5])'
iplist = re.findall(p, html)
for each in iplist:
print(each)
if __name__ == '__main__':
url = "https://ip.jiangxianli.com/"
# open_url(url)
get_img(open_url(url))
P62 论9:异常处理
本节内容:
- 处理URLError(访问网页异常)
- 见示例1
- 处理HTTPError,如状态码404
- HTTP 状态码大全
- 400-499的异常来自客户端,500-599异常来自服务器
- 示例2
- 两种常用的写法
- 示例3
# URLError 处理。示例1
import urllib.request
import urllib.error
req = urllib.request.Request("http://wwww.ooxx123.com") # 随便一个不存在的网址
try:
urllib.request.urlopen(req)
except urllib.error.URLError as e:
print(e.reason)
# HTTPError处理,如状态码404.示例2
import urllib.request
import urllib.error
req = urllib.request.Request("http://wwww.baidu.com/ooxx.html") # 随便一个不存在的网址
try:
urllib.request.urlopen(req)
except urllib.error.HTTPError as e:
print(e.code)
print(e.read()) # 读取错误页面,可对这个页面可以进行进一步处理
# 处理异常的两种写法,示例3
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError
req = Request('http://baidu.com') # Request(someurl)
try:
response = urlopen(req)
except HTTPError as e:
print('The server couldn\'t fulfill the request.')
print('Error code:', e.code)
except URLError as e:
print('We failed to reach a server')
print('Reason: ', e.reason)
else:
pass
# everything is fine
# 第二种
from urllib.request import Request, urlopen
from urllib.error import URLError
req = Request('http://baidu.com') # Request(someurl)
try:
response = urlopen(req)
except URLError as e:
if hasattr(e, 'reason'):
print('We failed to reach a server')
print('Reason: ', e.reason)
elif hasattr(e, 'code'):
print('The server couldn\'t fulfill the request.')
print('Error code:', e.code)
else:
pass
# everything is fine
P63 论10:安装 scrapy
Scrapy介绍:
Scrapy是用纯Python实现的一个爬取网站数据、提取结构性数据的应用框架。有了框架,用户只需要定制开发几个模块就可以轻松的实现一个爬虫,用来抓取网页内容以及各种图片。
最初是为了页面抓取所设计的,也可以应用在获取API所返回的数据或者通用的 网络爬虫。
- 优点:快速、高层次的屏幕抓取和web抓取框架
- 用途:数据挖掘、监测和自动化测试。
python如果安装多个版本并不冲突,他们独立存在
- 安装Scrapy所需要的前提和步骤
- python
- 大前提,也可以加上pycharme。
- pywin32 可提供一个像是PC一样的IDE
pip install pywin32
pip install lxml
pip install py``OpenSSL
pip install Scrapy
P64 论11:scrapy
使用Scrapy抓取一个网站的四个步骤
- 创建Scrapy项目
- 定义Item容器
- 编写爬虫
- 存储内容
Scrapy框架结构
绿色线条代表数据流
- 各个组件介绍
- Scrapy Engine(引擎):核心部分, 负责各组件之间的通讯。
- Downloader(下载器):从Scheduler(调度器)获取页面数据,交给Spider处理。
- Scheduler(调度器):接收从Scrapy Engine(引擎)发过来的请求,并排列整理。
- Spiders(爬虫):发送需要爬取的链接给Scrapy Engine(引擎),再接收请求回来的数据,通过用户自己编写的程序,去解析数据。
- Item Pipeline(管道):将Spider(爬虫)传递过来的数据进行存储。
- Downloader Middlewares(下载器中间件):可以扩展下载器和引擎之间通信功能的中间件。处理Downloaderr发到ScrapyEngine的Responses。
- Spider Middlewares(Spider中间件):可以扩展引擎和爬虫之间通信功能的中间件。
可参考scrapy框架
练手网站http://www.dmoz-odp.org/ 失效,而且像个八年用法有变,以后单独学习scrapy吧,scrapy学习笔记链接如下:
(scrapy待更新…)
🔰GUI(OLD:P64~P77)
P64 GUI的终极选择:Tkinter 1
Tkinter 是 Python 的标准 GUI 库。Python 使用 Tkinter 可以快速的创建 GUI 应用程序。
由于 Tkinter 是内置到 python 的安装包中、只要安装好 Python 之后就能 import Tkinter 库、而且 IDLE 也是用 Tkinter 编写而成、对于简单的图形界面 Tkinter 还是能应付自如。
注意:Python3.x 版本使用的库名为 tkinter,即首写字母 T 为小写。import tkinter
——Python GUI 编程(Tkinter) | 菜鸟教程
tkinter测试,示例1
# tkinter测试,示例1
import tkinter as tk
app = tk.Tk() # 创建top主窗口,注意大写T、小写k
app.title("FishC.demo") # top主窗口标题
theLable = tk.Label(app, text="第二个窗口程序") # 创建第二个窗口, Label 用于显示文本、图标、图片
theLable.pack() # 用于自动调节组件自身尺寸、位置
app.mainloop() # 窗口主事件循环
把GUI封装成类,示例2
import tkinter as tk
class App:
def __init__(self, root):
frame = tk.Frame(root) # 创建一个框架
frame.pack(side=tk.LEFT, padx=10, pady=10) # 设置左对齐、x轴间距0、y轴间距10
# 创建一个按钮组件,text为文本属性,bg为背景色,fg为前景色,command为按钮按下时要执行的命令属性
self.hi_there = tk.Button(frame, text="打招呼", bg="black", fg="white", command=self.say_hi)
self.hi_there.pack() # 自动调节自身尺寸
def say_hi(self):
print("互联网的广大朋友们好,我是小甲鱼!")
root = tk.Tk() # 创建一个top主窗口
app = App(root) # 实例化App
root.mainloop()
P65 GUI的终极选择:Tkinter 2
示例1 - 添加图片,
# 添加图片,示例1
from tkinter import *
root = Tk() # 创建主窗口
textLabel = Label( # 创建一个文本Label对象
root, # 在root上面显示Label
text="18🈲,\n请稍候再来!",
justify=LEFT, # 设置文字左对齐
padx=10
)
textLabel.pack(side=LEFT) # 设置文字排版
photo = PhotoImage(file="18.gif") # 创建一个图像Label对象,(这里只有gif的 可以,不然会报错_tkinter.TclError: couldn't recognize data in image file "")
imgLabel = Label(root, image=photo)
imgLabel.pack(side=RIGHT) # 设置图片排版
mainloop()
示例2 - 图片作为背景,文字显示上面,设置Label的compound就可以,
# 图片作为背景,文字显示上面,设置Label的compound就可以,示例2
from tkinter import *
root = Tk() # 创建主窗口
photo = PhotoImage(file="bg.gif")
theLabel = Label( # 创建一个文本Label对象
root, # 在root上面显示Label
text="学 python\n到 FishC!",
justify=LEFT, # 设置文字左对齐
image=photo, # 图片
compound=CENTER, # 文字显示在正上方
font=("华康少女字体", 50),
fg="black"
)
theLabel.pack()
mainloop()
示例3 - Button组件,改变Label文字内容,
'''
思路:
在原有的基础上,增加一个框架 frame 放置按钮
文本的改变,可通过 Label 的 textvariable 属性显示一个变量
'''
from tkinter import *
def callback():
var.set("吹吧你,我才不信呢~")
root = Tk()
frame1 = Frame(root)
frame2 = Frame(root)
var = StringVar()
var.set("您下载的影片含有未成年人限制内容,\n请满18周岁后再点击观看!")
textLabel = Label(frame1,
textvariable=var, # textvariable 可以显示一个变量
justify=LEFT)
textLabel.pack(side=LEFT)
photo = PhotoImage(file="18.gif")
imgLabel = Label(frame1, image=photo)
imgLabel.pack(side=RIGHT)
theButton = Button(frame2, text="我已满 18 周岁", command=callback)
theButton.pack()
frame1.pack(padx=10, pady=10)
frame2.pack(padx=10, pady=10)
mainloop()
- 小甲鱼在https://fishc.com发布的,供参考(付费内容…):
- Tkinter 窗口组件:Label
- Tkinter 窗口组件:Menu
- Tkinter 窗口组件:Text
- Tkinter 窗口组件:Listbox
- Tkinter 窗口组件:Button
- Tkinter 窗口组件:Entry
- Tkinter 窗口组件:Canvas
- Tkinter 窗口组件:Spinbox
- Tkinter 窗口组件:Toplevel
- Tkinter 窗口组件:LabelFrame
- Tkinter 窗口组件:PanedWindow
- Tkinter 窗口组件:Checkbutton
- Tkinter 窗口组件:Frame
- Tkinter 窗口组件:Scrollbar
- Tkinter 窗口组件:Radiobutton
- Tkinter 窗口组件:Message
- Tkinter 窗口组件:Scale
- Tkinter 窗口组件:Menubutton
- Tkinter 窗口组件:OptionMenu
P66 GUI的终极选择:Tkinter 3
示例1 - Checkbutton 组件,实现勾选 ,
# Checkbutton 组件,实现勾选 ,示例1
from tkinter import *
root = Tk()
v = IntVar() # Tkinter变量,用于表示该按钮是否被选中
c = Checkbutton(root, text="测试一下", variable=v)
c.pack()
lable = Label(root, textvariable=v) # v用来存放 Checkbutton 的选中状态
lable.pack()
mainloop()
示例2 - Checkbutton 多选,
# Checkbutton 多选,示例2
from tkinter import *
root = Tk()
GIRLS = ["西施", "貂蝉", "王昭君", "杨玉环"]
v = [] # 定义个空列表,存放勾选状态
for girl in GIRLS:
v.append(IntVar()) # 如果被选中,IntVar() 被赋值为1,否则为0
b = Checkbutton(root, text=girl, variable=v[-1])
b.pack(anchor=W) # anchor 用于指定显示位置,W=western=左对齐
mainloop()
示例3 - Radiobutton 单选按钮。
# Radiobutton 单选按钮。示例3
from tkinter import *
root = Tk()
v = IntVar()
Radiobutton(root, text="One", variable=v, value=1).pack(anchor=W) # 通过对比 variable 和value 的值,实现勾选的显示
Radiobutton(root, text="Two", variable=v, value=2).pack(anchor=W)
Radiobutton(root, text="Three", variable=v, value=3).pack(anchor=W)
mainloop()
示例4 - Radiobutton 单选按钮,用循环实现。
# Radiobutton 单选按钮,用循环实现。示例4
from tkinter import *
root = Tk()
LANGS = [
("Python", 1),
("Perl", 2),
("Ruby", 3),
("Lua", 4)]
v = IntVar()
v.set(1) # 设置默认值1
for lang, num in LANGS:
# b = Radiobutton(root, text=lang, variable=v, value=num)
# b.pack(anchor=W)
b = Radiobutton(root, text=lang, variable=v, value=num, indicatoron=False) # indicatoron=False 将显示为文字按钮效果
b.pack(fill=X) # X = 横向填充
mainloop()
示例5 - 用 LabelFrame 实现组件分组。
from tkinter import *
root = Tk()
group = LabelFrame(root, text="最好的脚本语言是?", padx=5, pady=5)
group.pack(padx=10, pady=10)
LANGS = [
("Python", 1),
("Perl", 2),
("Ruby", 3),
("Lua", 4)]
v = IntVar()
v.set(1)
for lang, num in LANGS:
b = Radiobutton(group, text=lang, variable=v, value=num)
b.pack(anchor=W)
mainloop()
P67 GUI的终极选择:Tkinter 4
输入框组件
示例1 - 输入框 Entry 组件,增加、删除。
- 增加、删除,用insert、delete。如示例1
- 获取输入框内容:
- 用 组件的get()
- 用 变量value的 get()
- grid 以表格形式管理组件。如示例2
# 输入框 Entry 组件,增加、删除。示例1
from tkinter import *
root = Tk()
e = Entry(root)
e.pack(padx=20, pady=20)
e.delete(0, END) # 清空输入框
e.insert(0, "默认文本...") # 设置输入内容
mainloop()
示例2 - 获取输入框内容。
# 获取输入框内容。示例2
from tkinter import *
root = Tk() # 创建主窗口
Label(root, text="作品:", padx=20, pady=10).grid(row=0, column=0) # grid(row=0, column=0)表示第0行第0列
Label(root, text="小甲鱼:").grid(row=1, column=0) # 第1行第0列
e1 = Entry(root) # 输入框
e2 = Entry(root) # 输入框
e1.grid(row=0, column=1, padx=10, pady=5)
e2.grid(row=1, column=1, padx=10, pady=5)
def button1_show():
print("作品:《%s》" % e1.get())
print("作者:%s" % e2.get())
Button(root, text="获取信息", command=button1_show)\
.grid(row=2, column=0, sticky=W, padx=10, pady=10) # sticky=W 表示位置在西(左)
Button(root, text="退出", command=root.quit)\
.grid(row=2, column=1, sticky=E, padx=10)
mainloop()
示例3 - 账号密码输入,获取。
# 账号密码输入,获取。示例3
from tkinter import *
root = Tk()
Label(root, text="账号:").grid(row=0, column=0)
Label(root, text="密码:").grid(row=1, column=0)
v1 = StringVar()
v2 = StringVar()
e1 = Entry(root, textvariable=v1)
e2 = Entry(root, textvariable=v2, show="*") # * 代替填充
e1.grid(row=0, column=1, padx=10, pady=5)
e2.grid(row=1, column=1, padx=10, pady=5)
def show():
print("账号:%s" % e1.get())
print("密码:%s" % e2.get())
Button(root, text="芝麻开门", command=show)\
.grid(row=2, column=0, sticky=W, padx=10, pady=5)
Button(root, text="退出", command=root.quit)\
.grid(row=2, column=1, sticky=E, padx=10)
mainloop()
示例4 - 输入验证。
启用验证开关:validate选项
验证函数:validatecommand,返回True或False。通过Entry组件的 get() 方法获得该验证字符串
# 输入验证。示例4
# 在第一个输入框输入,并通过tab键将焦点转移到第二个输入框时,验证功能被触发
from tkinter import *
master = Tk()
def test():
if e1.get() == "小甲鱼":
print("正确!")
return True
else:
print("错误!")
e1.delete(0, END) # 清空输入框
return False
v = StringVar()
e1 = Entry(master, textvariable=v, validate="focusout", validatecommand=test) # validate="focusout"表示焦点移出时,validatecommand=test自动触发验证函数
e2 = Entry(master)
e1.pack(padx=10, pady=10)
e2.pack(padx=10, pady=10)
mainloop()
示例5 - invalidcommand函数。
invalidcommand函数只有在validatecommand的返回值 为False时才被调用
# invalidcommand函数。示例5
from tkinter import *
master = Tk()
v = StringVar()
def test():
if e1.get() == "小甲鱼":
print("正确!")
return True
else:
print("错误!")
e1.delete(0, END)
return False
def test2():
print("我被调用了...")
e1 = Entry(master, textvariable=v, validate="focusout",\
validatecommand=test, invalidcommand=test2)
e2 = Entry(master)
e1.pack(padx=10, pady=10)
e2.pack(padx=10, pady=10)
mainloop()
示例6 - validatecommand的其他参数。
- validatecommand的参数
- %P 当输入框的值允许改变的时候,该值有效;该值为输入框的最新文本内容
- %v 该组件当前的 validata 选项的值
- %W 该组件的名称
# validatecommand的其他参数。示例6
from tkinter import *
master = Tk()
v = StringVar()
def test(content, reason, name):
if content == "小甲鱼":
print("正确!")
print(content, reason, name)
return True
else:
print("错误!")
print(content, reason, name)
return False
testCMD = master.register(test)
e1 = Entry(master, textvariable=v, validate="focusout",\
validatecommand=(testCMD, '%P', '%v', '%W'))
e2 = Entry(master)
e1.pack(padx=10, pady=10)
e2.pack(padx=10, pady=10)
mainloop()
示例7 - 计算器。
validate选项指定为“key"的时候,有任何输入操作都会被拦截去验证,如果返回True,才会给关联的变量textvariable赋值
- state:
- Entry可以设置的状态:NORMAL,DISABLED(禁用选中、拷贝、修改)或"readonly"(支持选中和拷贝,不能修改)
- 默认值时NORMAL
- 如果值为DISABLED或"readonly",那么调用 insert() 和 delete() 都会被忽略
# 计算器。示例7
from tkinter import *
root = Tk()
frame = Frame(root)
frame.pack(padx=10, pady=10)
v1 = StringVar() # 第一个数
v2 = StringVar() # 第二个数
v3 = StringVar() # 计算结果
def test(content):
return content.isdigit()
testCMD = frame.register(test)
e1 = Entry(frame, width=10, textvariable=v1, validate="key",validatecommand=(testCMD, '%P'))\
.grid(row=0, column=0)
Label(frame, text="+").grid(row=0, column=1) # 显示 +
e2 = Entry(frame, width=10, textvariable=v2, validate="key",validatecommand=(testCMD, '%P'))\
.grid(row=0, column=2)
Label(frame, text="=").grid(row=0, column=3) # 显示 =
e3 = Entry(frame, width=10, textvariable=v3, state="readonly")\
.grid(row=0, column=4) # state="readonly" = 只读模式,v3的数据赋值给textvariable进行输出显示
def calc():
result = int(v1.get()) + int(v2.get())
v3.set(str(result))
Button(frame, text="计算结果", command=calc).grid(row=1, column=2, pady=5)
mainloop()
P68 GUI的终极选择:Tkinter 5
Listbox组件
列表框+滚动条 = 支持大量选项的情况下
示例1 - Listbox简单示例,添加、删除。
- 添加 .insert()
- 删除 .delete()
- selectmode选项的四种选择模式
- SINGLE 单选
- BROWSE 单选,通过鼠标拖动或方向键
- MULTIPLE 多选
- EXTENDED 多选,但需同时按住Shift或Ctrl
# Listbox简单示例,添加、删除。示例1
from tkinter import *
master = Tk()#创建主窗口
theLB = Listbox(master, setgrid=True, selectmode=SINGLE)
theLB.pack()
# 添加,基础方法
# theLB.insert(0, '你是猪') # 0 表示第一个位置
# theLB.insert(END, '小甲鱼不是猪') # END 表示最后一个位置
# 添加,循环添加
for item in ["鸡蛋", "鸭蛋", "鹅蛋", "李狗蛋"]:
theLB.insert(END, item) # END表示列表中每一项的最后位置
# 删除,基础方法
# theLB.delete(0, END) # 删除所有项目,两个参数分别是起始位置、结束位置
# theLB.delete(2) # 删除指定
# 删除,通过按钮删除
theButton = Button(master, text="删除它", command=lambda x=theLB:x.delete(ACTIVE)) # ACTIVE表示当前选中的数据
theButton.pack() # ↑ lambda表达式
mainloop()
示例2 - height 选项控制行数。
# height 选项控制行数。示例2
from tkinter import *
root = Tk()
theLB = Listbox(root, setgrid=True, selectmode=BROWSE, height=11) # height=11 显示11行
theLB.pack()
for item in range(11):
theLB.insert(END, item)
theButton = Button(root, text="删除", command=lambda x=theLB: x.delete(ACTIVE))
theButton.pack()
mainloop()
示例3 - 滚动条 Scrollbar组件。
- 在组件上安装垂直滚动条:
- 设置该组件的yscrollbarcommand选项为Scrollbar组件的set()方法
- 设置Scrallbar组建的command选项为该组件的yview()方法,才能将滚动条与列表相链接
# 滚动条 Scrollbar组件。示例3
from tkinter import *
root = Tk()
sb = Scrollbar(root)
sb.pack(side=RIGHT, fill=Y) # 右边,填充整个Y轴
lb = Listbox(root, yscrollcommand=sb.set)
for i in range(1000):
lb.insert(END, i)
lb.pack(side=LEFT, fill=BOTH)
sb.config(command=lb.yview) # yview 是Listbox的一个默认方法,能自动跟随垂直滚动
mainloop()
示例4 - 滑块 Scale组件。
- Scale组件是;
- 通过滑块来表示某个范围内的值
- 通过Scale(root, from_=0, to=100),显示范围
- 通过get方法,获取滑块当前位置
# 滑块 Scale组件。示例4
from tkinter import *
root = Tk()
Scale(root, from_=0, to=42).pack() # from_=0, to=42 表示范围
Scale(root, from_=0, to=200, orient=HORIZONTAL).pack() # orient=HORIZONTAL水平显示
mainloop()
示例5 - 通过get方法,获取滑块当前位置。
# 通过get方法,获取滑块当前位置。示例5
from tkinter import *
root = Tk()
s1 = Scale(root, from_=0, to=42)
s1.pack()
s2 = Scale(root, from_=0, to=200, orient=HORIZONTAL)
s2.pack()
def show():
print(s1.get(), s2.get())
Button(root, text="获取位置", command=show).pack()
mainloop()
示例6 - 设置精度和刻度。
- 通过 resolution 选项,控制精度(步长)
- 通过 tickinterval 选项,设置刻度
# 设置精度和刻度。示例6
from tkinter import *
root = Tk()
# tickinterval=5 最小刻度是5;resolution=5 每个5个数值,走一步;length=200 设置滚动条长度
Scale(root, from_=0, to=42, tickinterval=5, length=200, resolution=5, orient=VERTICAL).pack() #创建铅锤方向滚动条
Scale(root,from_=0, to=200, tickinterval=10, length=600, orient=HORIZONTAL).pack()
mainloop()
P69 GUI的终极选择:Tkinter 6
示例1 - Text组件简单示例
- Text组件 Text(文本)
- 作用:用于显示和处理多行文本
- 常也用于作为简单的文本编辑器和网页浏览器使用
# Text组件简单示例 示例1
from tkinter import *
root = Tk()
text = Text(root, width=30, height=2) # width=30 30个字符平均宽度;height=2 两行
text.pack()
text.insert(INSERT, "I love\n") # INSERT索引值,表示输入光标所在位置
text.insert(END, "FishC.com!") # END索引值,表示在文本末尾插入内容
mainloop()
示例2 - 插入Windows组件
# 插入Windows组件 示例2
from tkinter import *
root = Tk()
def show():
print("哟,我被点了一下~")
text = Text(root, width=30, height=5)
text.pack()
text.insert(INSERT, "I love\n")
text.insert(END, "FishC.com!")
b1 = Button(text, text="点我点我", command=show) # Button(父组件, , ...)
text.window_create(INSERT, window=b1)
mainloop()
示例3 - 插入image对象
# 插入image对象 示例3
from tkinter import *
root = Tk()
text = Text(root, width=80, height=30)
text.pack()
photo = PhotoImage(file='fishc.gif')
def show():
text.image_create(INSERT, image=photo)
b1 = Button(root, text="点我点我", command=show)
text.window_create(INSERT, window=b1)
mainloop()
- Indexer用法
- lin.column
- 最基础的索引方式
- 将行列号以字符串形式表示,如“1.0”
- 行号以 1 开始,列号以 0 开始
- lin.end
- 表示该行最后一个字符的位置
- 用法如:text.get(1.2, 1.end)
- INSERT
- 光标位置
- CURRENT
- 与鼠标坐标最接近的位置
- END
- Text 组件最后一个字符的下一个位置
# line.column 示例4
from tkinter import *
root = Tk()
text = Text(root, width=30, height=30)
text.pack()
text.insert(INSERT, "I love FishC.com!")
print(text.get(1.2, 1.12))
print(text.get("1.2", "1.end"))
mainloop()
示例5 - Marks用法。
Marks用法
- 嵌入到Text组件中的不可见对象
- 指定字符间的位置,并跟随相应的字符一起移动。
- 用法如:text.mark_set(“here”, “1.2”) text.insert(“here”, “插入”)
- mark能记住它后面是谁,下次插入找到后面那个往前插;通过mark_gravity()可以实现找到后面那个往后插
# Marks用法。示例5
from tkinter import *
root = Tk()
text = Text(root, width=30, height=30)
text.pack()
text.insert(INSERT, "I love FishC.com!") # 光标当前的位置插入
text.mark_set("here", "1.2") # 设置光标位置为1.2
text.insert("here", "插1")
text.insert("here", "插2") # Mark的位置会跟随移动
text.insert("here", "入") # Mark的位置也会跟着移动
# text.delete("1.0", END)
# text.insert("here", "插3") # 如果Mark周围的文本被删除了,Mark仍然存在.默认插在1.0的位置
# text.mark_unset("here") # mark_unset()方法可以解除Mark,解除后再插入将报错
# text.insert("here", "插4") # here表示当前Mark的位置
text.mark_gravity("here", LEFT) # 插入内容到Mark的左侧,而不是mark后面内容的左侧
text.insert("here", "插5")
text.insert("here", "入")
mainloop()
P70 GUI的终极选择:Tkinter 7
Tags 用法
- 作用:
- 改变Text组件中的内容样式和功能,如:修改文本字体、尺寸、颜色
- 将文本、嵌入的组件、图片与键盘鼠标等事件相关联
- 类型:
- user-defined tags 用户自定义Tags
- SEL 预定义的特殊Tag
- SEL 用于表示对应的选中内容
- 定义
- 用户可自定义任意数量的Tags
- 任何文本内容都支持多个Tags描述
- 任何Tag 也可用于描述多个不同的文本内容
- 用法
- 添加标签
- text.tag_add()
- 设置标签
- text.tag_config()
- 注意
- 如果对同一个范围的文本内容添加了多个相同的tags,那么,旧的tag样式会被覆盖。但是可通过tag_raise()、tag_lower()改变tag的优先级
示例1 - 为文本添加、设置 Tags
# 为文本添加、设置 Tags 示例1
from tkinter import *
root = Tk()
text = Text(root, width=30, height=5)
text.pack()
text.insert(INSERT, "I love FishC.com!")
text.tag_add("tag1", "1.7", "1.12", "1.14") # "1.7" 第一行第八列
text.tag_config("tag1", background="yellow", foreground="red")
mainloop()
'''
'''
# Tags被覆盖,演示
from tkinter import *
root = Tk()
text = Text(root, width=30, height=5)
text.pack()
text.insert(INSERT, "I love FishC.com!")
text.tag_add("tag1", "1.7", "1.12", "1.14")
text.tag_add("tag2", "1.7", "1.12", "1.14")
text.tag_config("tag1", background="yellow", foreground="red")
text.tag_config("tag2", background="blue")
# text.tag_lower("tag2") # 降低tag2的优先级
mainloop()
'''
示例2 - Tags事件绑定
# Tags事件绑定 示例2
from tkinter import *
import webbrowser # 网页模块
root = Tk()
text = Text(root, width=30, height=5)
text.pack()
text.insert(INSERT, "I love FishC.com!")
text.tag_add("link", "1.7", "1.16")
text.tag_config("link", foreground="blue", underline=True) # 设置蓝色前景色并底部划线 + 下划线
def show_arrow_cursor(event):
text.config(cursor="arrow")
def show_xterm_cursor(event):
text.config(cursor="xterm")
def click(event):
webbrowser.open("http://www.fishc.com")
text.tag_bind("link", "<Enter>", show_arrow_cursor) # 绑定事件。 "<Enter>" 当鼠标进入。show_hand_cursor 调用的函数
text.tag_bind("link", "<Leave>", show_xterm_cursor)
text.tag_bind("link", "<Button-1>", click) # 当触发鼠标“左键单击”时,使用默认浏览器打开鱼C网址
mainloop()
示例3 - 检查内容是否发生改变
获取md5,对比摘要
# 检查内容是否发生改变 示例3
from tkinter import *
import hashlib
root = Tk()
text = Text(root, width=30, height=5)
text.pack()
text.insert(INSERT, "I love FishC.com!")
contents = text.get(1.0, END) # 获取文本内容
def getSig(contents):
m = hashlib.md5(contents.encode()) # 获取md5的值
return m.digest() # 得到摘要
sig = getSig(contents)
def check(): # 检查
contents = text.get(1.0, END)
if sig != getSig(contents):
print("警报,内容发生变动")
else:
print("风平浪静")
Button(root, text="检查", command=check).pack()
mainloop()
示例4 - 查找
- 查找
- search() 搜索Text组件中的内容
- 只能返回第一个匹配的位置,如果要全文搜索 加循环即可
# 查找 示例4
from tkinter import *
root = Tk()
text = Text(root, width=30, height=5)
text.pack()
text.insert(INSERT, "I love FishC.com!")
def getIndex(text, index): # index可将任何支持的格式转化为元组返回
return tuple(map(int, str.split(text.index(index), ".")))
start = 1.0
while True:
pos = text.search("o", start, stopindex=END)
if not pos:
break
print("找到了,位置是:", getIndex(text, pos))
start = pos + "+1c" # 将start指向找到的字符位置的下一个字符,以便进行下一次搜索
mainloop()
示例5 - 撤销
撤销删除文本的操作
- 首先要将Text()方法中写入 undo=True
- 输入文本就是入栈,“撤销”就是一次弹栈,“恢复”就是再次入栈。
自定义撤销的长度
- 默认的长度是:”焦点切换“、”按下Enter“、删除等操作代表的一次完整操作
- 自定义
- 先将将Text()方法中写入 autoseparators=False
# 撤销 示例5
from tkinter import *
root = Tk()
text = Text(root, width=30, height=5, undo=True)
text.pack()
text.insert(INSERT, "I love FishC.com")
def show():
text.edit_undo()
Button(root, text="撤销", command=show).pack()
mainloop()
'''
# 每次撤销一个字符
from tkinter import *
root = Tk()
text = Text(root, width=30, height=5, undo=True, autoseparators=False)
text.pack()
text.insert(INSERT, "I love FishC.com")
def callback(event):
text.edit_separator()
text.bind('<Key>', callback) # 每次输入就插入一个分隔符
def show():
text.edit_undo()
Button(root, text="撤销", command=show).pack()
mainloop()
'''
P71 GUI的终极选择:Tkinter 8
Canvas(画布)组件
- 用于显示和编辑图形
- 可 绘制直线、图形、多边形
示例1 - 开始绘制
开始绘制
- 直线 create_line(x0, y0, x1, y1)
- 矩形 create_rectangle(x0, y0, x1, y1)
# 开始绘制 示例1
from tkinter import *
root = Tk()
w = Canvas(root, width=200, height=100) # 创建canvas对象
w.pack()
w.create_line(0, 50, 200, 50, fill="yellow") # 黄色实线
w.create_line(100, 0, 100, 100, fill="red", dash=(4, 4)) # 红色虚线
w.create_rectangle(50, 25, 150, 75, fill="blue") # 蓝色矩形
mainloop()
示例2 - 修改图形
修改图像的方法:
- .coords(移动对象,x0, y0, x1, y1) 移动到新的位置
- .itemconfig(对象, 选项样式, …) 设置样式
- .move
- .delete(对象)
# 修改图形 示例2
from tkinter import *
root = Tk()
w = Canvas(root, width=200, height=100) # 创建canvas对象
w.pack()
line1 = w.create_line(0, 50, 200, 50, fill="yellow") # 黄色实线
line2 = w.create_line(100, 0, 100, 100, fill="red", dash=(4, 4)) # 红色虚线
rect1 = w.create_rectangle(50, 25, 150, 75, fill="blue") # 蓝色矩形
w.coords(line1, 0, 25, 200, 25) # 将line1移动到新的位置
w.itemconfig(rect1, fill="red") # 设置矩形填充色红色
w.delete(line2) # 删除line2
Button(root, text="删除全部", command=(lambda x=ALL: w.delete(x))).pack() # 按钮,删除所有图形;ALL表示画布所有对象
mainloop()
示例3 - 在 Canvas 上显示文本
显示文本
- create_text(x0, y0, text="")
# 在 Canvas 上显示文本 示例3
from tkinter import *
root = Tk()
w = Canvas(root, width=200, height=100) # 创建canvas对象
w.pack()
line1 = w.create_line(0, 0, 200, 100, fill="green", width=3) # 绿色的斜线
line2 = w.create_line(200, 0, 0, 100, fill="green", width=3) # 色的斜线
rect1 = w.create_rectangle(40, 20, 160, 80, fill="blue") # 蓝色矩形
rect2 = w.create_rectangle(60, 35, 145, 65, fill="yellow") # 蓝色矩形
w.create_text(100, 50, text="FishC")
mainloop()
示例4 - 绘制矩形、圆与椭圆
绘制矩形
- .create_rectangle
绘制椭圆- .create_oval
- 在同尺寸的矩形下的椭圆
绘制圆形- 特殊椭圆法
- 将限定矩形改为正方形
# 绘制矩形、圆与椭圆 示例4
from tkinter import *
root = Tk()
w = Canvas(root,width=200,height=100,background="white")
w.pack()
w.create_rectangle(40, 20, 160, 80, dash=(4, 4)) # 虚线的矩形
w.create_oval(40, 20, 160, 80, fill="pink") # 椭圆,粉色填充
w.create_oval(70, 20, 130, 80, fill="black") # 圆形
mainloop()
示例5 - 多边形-五角星
绘制多边形
- create_polygon
# 多边形-五角星 示例5
from tkinter import *
import math as m
root = Tk()
w = Canvas(root, width=200, height=150, background="red")
w.pack()
center_x = 100
center_y = 80
r = 70
points = [
# 左上点
center_x - int(r*m.sin(2*m.pi/5)),
center_y - int(r*m.cos(2*m.pi/5)),
# 右上点
center_x + int(r*m.sin(2*m.pi/5)),
center_y - int(r*m.cos(2*m.pi/5)),
# 左下点
center_x - int(r*m.sin(m.pi/5)),
center_y + int(r*m.cos(m.pi/5)),
# 顶点
center_x,
center_y - r,
# 右下点
center_x + int(r*m.sin(m.pi/5)),
center_y + int(r*m.cos(m.pi/5)),
]
w.create_polygon(points, outline="green", fill="yellow")
mainloop()
P72 GUI的终极选择:Tkinter 9
Canvas实现画板
- 利用超小圆形代笔一个画笔的点
from tkinter import *
root = Tk()
w = Canvas(root, width=500, height=800, background="white")
w.pack()
def paint(event):
x1, y1 = (event.x - 1), (event.y - 1)
x2, y2 = (event.x + 5), (event.y + 5)
w.create_oval(x1, y1, x2, y2, fill="red")
w.bind("<B1-Motion>", paint) # 画布与鼠标左键绑定paint方法
Label(root, text="按住鼠标左键并移动,开始绘制你的理想蓝图吧......").pack(side=BOTTOM)
mainloop()
Canvas组件支持对象
- arc 弧形、弦、扇形
- bitmap 内建的位图文件或XBM格式文件
- image BitmapImage、PhotoImage 的实例对象
- line 线
- oval 圆形、椭圆
- polygon 多边形
- rectangle 矩形
- text 文本
- windows 组件
样式
- 轮廓线
- 填充颜色 fill = “”
坐标系
- 窗口坐标系: 以窗口左上角为坐标原点
- 画布坐标系: 以画布左上角为坐标原点
- 转换
- canvasx()
- canvasy()
显示顺序
- 可以移动到顶部或底部
指定画布对象
- Iteam handles: 指定画布对象的整型数字,如ID
- Tags: 标签
- ALL: 所有画布对象
- CURRENT: 鼠标指针下的画布对象
P73 GUI的终极选择:Tkinter 10
示例1 - 添加菜单
创建菜单用 Menu 组件
功能:
- 现顶级菜单、下拉菜单、弹出菜单
添加组件- .add()
- .add_command(label="", command= )
# 添加菜单 示例1
from tkinter import *
root = Tk()
def callback():
print("你好~")
menubar = Menu(root) # 创建菜单
menubar.add_command(label="hello", command=callback) # 创建菜单对象
menubar.add_command(label="quit", command=root.quit)
root.config(menu=menubar) # (必须)将菜单与窗口关联,不然不显示
mainloop()
示例2 - 创建一组菜单
# 创建一组菜单 示例2
from tkinter import *
root = Tk()
def callback():
print("你好~")
menubar = Menu(root)
filemenu = Menu(menubar, tearoff=False)
filemenu.add_command(label="打开", command=callback) # 创建一个下拉菜单对象
filemenu.add_command(label="保存", command=callback)
filemenu.add_separator() # 插入分隔线
filemenu.add_command(label="退出", command=root.quit)
menubar.add_cascade(label="文件", menu=filemenu) # 级联一级菜单,下级菜单是 filemenu
editmenu = Menu(menubar, tearoff=False)
editmenu.add_command(label="剪切", command=callback)
editmenu.add_command(label="拷贝", command=callback)
editmenu.add_command(label="粘贴", command=callback)
menubar.add_cascade(label="编辑", menu=editmenu) # 级联一级菜单,下级菜单是 editmenu
root.config(menu=menubar)
mainloop()
示例3 - 创建右键弹出菜单
# 创建右键弹出菜单 示例3
from tkinter import *
root = Tk()
def callback():
print("你好~")
def popup(event):
menu.post(event.x_root, event.y_root) # 在指定位置弹出菜单
menu = Menu(root, tearoff=False) # 绑定 root, tearoff=True能将菜单放到新窗口
menu.add_command(label="撤销", command=callback)
menu.add_command(label="重做", command=callback)
frame = Frame(root, width=512, height=512) # 创建一个框架
frame.pack()
frame.bind("<Button-3>", popup) # 绑定 popup,<Button-3> 是鼠标中间键
mainloop()
示例4 - 带有按钮的菜单
菜单中可添加单选、多选按钮radiobutton checkbutton
# 带有按钮的菜单 示例4
from tkinter import *
root = Tk()
def callback():
print("你好~")
menubar = Menu(root)
openVar = IntVar() # 存放按钮当前的状态
saveVar = IntVar()
exitVar = IntVar()
filemenu = Menu(menubar, tearoff=False)
filemenu.add_checkbutton(label="打开", command=callback, variable=openVar) # 创建一个下拉菜单对象
filemenu.add_checkbutton(label="保存", command=callback, variable=saveVar)
filemenu.add_separator() # 插入分隔线
filemenu.add_checkbutton(label="退出", command=root.quit, variable=exitVar)
menubar.add_cascade(label="文件", menu=filemenu) # 级联一级菜单,下级菜单是 filemenu
editVar = IntVar()
editmenu = Menu(menubar, tearoff=False)
editmenu.add_radiobutton(label="剪切", command=callback, variable=editVar, value=1)
editmenu.add_radiobutton(label="拷贝", command=callback, variable=editVar, value=2)
editmenu.add_radiobutton(label="粘贴", command=callback, variable=editVar, value=3)
menubar.add_cascade(label="编辑", menu=editmenu) # 级联一级菜单,下级菜单是 editmenu
root.config(menu=menubar)
mainloop()
示例5 - Menubutton
Menubutton 组件
button可以出现在任何位置,点开是一个menu
# Menubutton 示例5
from tkinter import *
root = Tk()
def callback():
print("你好~")
mb = Menubutton(root, text="点我", relief=RAISED)
mb.pack()
filemenu = Menu(mb, tearoff=False)
filemenu.add_command(label="打开", command=callback) # 创建一个下拉菜单对象
filemenu.add_command(label="保存", command=callback)
filemenu.add_separator() # 插入分隔线
filemenu.add_command(label="退出", command=root.quit)
mb.config(menu=filemenu)
mainloop()
# OptionMenu 菜单中的选项 示例6
from tkinter import *
root = Tk()
def callback():
print("你好~")
variable = StringVar() # 字符串变量variable
variable.set("one") # 初始值为"one"
w = OptionMenu(root, variable, "one", "two", "three")
w.pack()
mainloop()
示例7 - OptionMenu菜单 中 添加列表
# OptionMenu菜单 中 添加列表 示例7
from tkinter import *
root = Tk()
OPTIONS = [
"1",
"2",
"3",
"4"
]
def callback():
print("你好~")
variable = StringVar() # 字符串变量variable
variable.set("one") # 初始值为"one"
w = OptionMenu(root, variable, *OPTIONS) # * 用来解包,将列表内容变为一个个单独的数据
w.pack()
mainloop()
P74 GUI的终极选择:Tkinter 11
示例1 - 事件绑定简单示例 - 获取鼠标左键点击相对坐标
事件绑定
- 创建一个frame框架,在框架中响应事件
- 如:
- 左边是事件本身、右边是事件描述。Button 代表鼠标点击事件、1 代表鼠标左键(2为中间键,3为右键)
- 按键响应
# 事件绑定简单示例 - 获取鼠标左键点击相对坐标 - 示例1
from tkinter import *
root = Tk()
def callback(event):
print("点击位置:", event.x, event.y)
frame = Frame(root, width=200, height=200)
frame.bind("<Button-1>", callback) # <Button-1> 左边是事件本身、右边是事件描述;绑定callback方法
frame.pack()
mainloop()
示例2 - 响应键盘事件-获取键盘输入
必须先获得焦点,才能响应键盘。用.focus_set()
from tkinter import *
root = Tk()
def callback(event):
print(event.char)
frame = Frame(root, width=200, height=200)
frame.bind("<Key>", callback) # 绑定callback方法
frame.focus_set()
frame.pack()
mainloop()
示例3 - 响应鼠标事件-获取鼠标实时位置
当鼠标进入组件中,触发事件响应
# 响应鼠标事件-获取鼠标实时位置-示例3
from tkinter import *
root = Tk()
def callback(event):
print("当前位置:", event.x, event.y)
frame = Frame(root, width=200, height=200)
frame.bind("<Motion>", callback) # 绑定callback方法
frame.focus_set()
frame.pack()
mainloop()
P75 GUI的终极选择:Tkinter 12
示例1 - Message 组件
Message 组件
- 功能:
- 用于显示多行文本
- 能够自动换行,并调整文本的尺寸
# Message 组件 - 示例1
from tkinter import *
root = Tk()
w1 = Message(root, text="这是一则消息", width=100)
w1.pack()
w2 = Message(root, text="这是一条骇人听闻的长~~~~~~~~~~~~~~~~~消息!", width=100)
w2.pack()
mainloop()
示例2 - Spinbox 组件
Spinbox 组件
- 功能:
- 限定用户输入的内容
# Spinbox 组件 - 示例2
from tkinter import *
root = Tk()
w = Spinbox(root, from_=0, to=10, increment=0.5) # 限定输入为0-10,increment=0.5 精度为0.5
# w = Spinbox(root, value=("a", "b", "3"))
w.pack()
mainloop()
示例3 - PanedWindow 组件 - 二窗格、三窗格
PanedWindow 组件
- 功能:
- 空间管理,为子组件提供一个框架
# PanedWindow 组件 - 二窗格、三窗格 - 示例3
from tkinter import *
root = Tk()
m = PanedWindow(orient=VERTICAL) # orient=VERTICAL 设置为上下分布
m.pack(fill=BOTH, expand=1) # 设置为框架覆盖全局
top = Label(m, text="top pane") # 顶窗格
m.add(top)
bottom = Label(m, text="bottom pane") # 底窗格
m.add(bottom)
mainloop()
'''
# 三窗格
from tkinter import *
root = Tk()
m1 = PanedWindow()
m1.pack(fill=BOTH, expand=1)
left = Label(m1, text="left pane") # 左窗格
m1.add(left)
m2 = PanedWindow(orient=VERTICAL)
m1.add(m2)
top = Label(m2, text="top pane") # 顶窗格
m2.add(top)
bottom = Label(m2, text="bottom pane") # 底窗格
m2.add(bottom)
mainloop()
'''
'''
# 显示线条边界
from tkinter import *
root = Tk()
m1 = PanedWindow(showhandle=True, sashrelief=SUNKEN) # showhandle=True 显示手柄;sashrelief=SUNKEN 样式为向下凹
m1.pack(fill=BOTH, expand=1)
left = Label(m1, text="left pane") # 左窗格
m1.add(left)
m2 = PanedWindow(orient=VERTICAL, showhandle=True, sashrelief=SUNKEN)
m1.add(m2)
top = Label(m2, text="top pane") # 顶窗格
m2.add(top)
bottom = Label(m2, text="bottom pane") # 底窗格
m2.add(bottom)
mainloop()
'''
示例4 - Toplevel 组件
Toplevel 组件
- 独立的顶级窗口,通常拥有标题栏、边框等部件
- 功能:
- 显示额外的窗口、对话框和其他弹出窗口
# Toplevel 组件 - 示例4
from tkinter import *
root = Tk()
def create():
top = Toplevel()
top.title("FishC Demo")
msg = Message(top, text="I love FishC.com", width=100)
msg.pack()
Button(root, text="创建顶级窗口", command=create).pack()
mainloop()
'''
# 设置透明度
from tkinter import *
root = Tk()
def create():
top = Toplevel()
top.title("FishC Demo")
top.attributes("-alpha", 0.8) # 设置为80%透明度
msg = Message(top, text="I love FishC.com", width=100)
msg.pack()
Button(root, text="创建顶级窗口", command=create).pack()
mainloop()
'''
P76 GUI的终极选择:Tkinter 13
布局管理器
管理组件如何排列
三大布局管理器
- pack
- 按照添加顺序排列(默认纵向排列)
- 简单;适用于少量的组件
- grid
- 按照行列行hi排列
- 最灵活、最重要
- 可实现用很多个框架和pack搭建起来的效果
- 使用grid排列组件,只需告诉它你想要将组件放置的位置(行row/列column)。
- place
- 允许程序员指定组件的大小、位置
注意:
- pack 和 grid 不能混合使用
示例1 - pack
# pack - 示例1
from tkinter import *
root = Tk()
listbox = Listbox(root)
listbox.pack(fill=BOTH, expand=True) # fill=BOTH 紧挨着父组件;expand=True 拉伸时跟随填充
for i in range(10):
listbox.insert(END, str(i))
mainloop()
示例2 - pack - 纵向排列 fill=X
# pack - 纵向排列 fill=X - 示例2
from tkinter import *
root = Tk()
Label(root, text="red", bg="red", fg="white").pack(fill=X)
Label(root, text="green", bg="green", fg="black").pack(fill=X)
Label(root, text="blue", bg="blue", fg="white").pack(fill=X)
mainloop()
示例3 - pack - 横向排列 side=LEFT
# pack - 横向排列 side=LEFT - 示例3
from tkinter import *
root = Tk()
Label(root, text="red", bg="red", fg="white").pack(side=LEFT)
Label(root, text="green", bg="green", fg="black").pack(side=LEFT)
Label(root, text="blue", bg="blue", fg="white").pack(side=LEFT)
mainloop()
示例4 - grid - 登录页面示例
# grid - 登录页面示例 - 示例4
from tkinter import *
root = Tk()
Label(root, text="用户名").grid(row=0, sticky=W) # sticky=W 左对齐
Label(root, text="密码").grid(row=1, sticky=W)
Entry(root).grid(row=0, column=1)
Entry(root, show="*").grid(row=1, column=1)
mainloop()
示例5 - grid - 跨行、跨列
# grid - 跨行、跨列 - 示例5
from tkinter import *
root = Tk()
Label(root, text="用户名").grid(row=0, sticky=W) # sticky=W 左对齐
Label(root, text="密码").grid(row=1, sticky=W)
Entry(root).grid(row=0, column=1)
Entry(root, show="*").grid(row=1, column=1)
photo = PhotoImage(file="18.gif") # 插入Label图像
Label(root, image=photo).grid(row=0, column=2, rowspan=2, padx=5, pady=5) # rowspan=2 跨两行;padx=5, pady=5 边距
Button(text="提交", width=10).grid(row=2, columnspan=3, pady=5)
mainloop()
示例6 - place - 将子组件显示在父组件的正中间
# place - 将子组件显示在父组件的正中间 - 示例6
from tkinter import *
root = Tk()
def callback():
print("正中靶心")
Button(root, text="点我", command=callback).place(relx=0.5, rely=0.5, anchor=CENTER) # relx=0.5, rely=0.5 相对父组件的位置(0.5表示正中间,1表示最右边,0表示最左边);
# anchor=CENTER 表示居中显示
mainloop()
示例7 - place - 图像中显示label
# place - 图像中显示label - 示例7
from tkinter import *
root = Tk()
def callback():
print("正中靶心")
photo = PhotoImage(file="logo_big.gif")
Label(root,image=photo).pack()
Button(root, text="点我", command=callback).place(relx=0.5, rely=0.5, anchor=CENTER)
mainloop()
示例8 - place - relheigh、relwidth 相对高度,相对宽度
# place - relheigh、relwidth 相对高度,相对宽度 - 示例8
from tkinter import *
root = Tk()
Label(root, bg="red").place(relx=0.5, rely=0.5, relheight=0.75, relwidth=0.75, anchor=CENTER)
Label(root, bg="yellow").place(relx=0.5, rely=0.5, relheight=0.5, relwidth=0.5, anchor=CENTER)
Label(root, bg="green").place(relx=0.5, rely=0.5, relheight=0.25, relwidth=0.25, anchor=CENTER)
mainloop()
P77 GUI的终极选择:Tkinter 14
示例1 - messagebox.askokcancel 消息对话框
messagebox.askokcancel(title, message, options)
- title 设置标题栏文本
- message 设置对话框主要文本内容,可使用\n
- options
- default
- icon
- parent
返回值:
- ture: 用户按下了 yes
- false: 用户按下了 no
# messagebox.askokcancel 消息对话框 示例1
from tkinter import *
from tkinter import messagebox
messagebox.askokcancel("FishC Demo", "发射核弹?")
mainloop()
示例2 - 文件对话框
filedialog.askopenfilename() 打开文件
参数:
- defaultextension 指定文件名后缀
- filetypes 指定筛选文件类型的下拉菜单选项
- initialdir 指定打开/保存文件的默认路径
- parent
- title
返回值:- 返回文件完整路径: 用户选择了文件
- 空字符串: 用户点击了取消
filedialog.asksavefilename() 保存文件
# 文件对话框 示例2
from tkinter import *
from tkinter import filedialog
root = Tk()
def callback():
# fileName = filedialog.askopenfilename(defaultextension=".gif")
fileName = filedialog.askopenfilename(filetypes=[("GIF", ".gif"), ("Python", ".py")])
print(fileName)
Button(root, text="打开文件", command=callback).pack()
mainloop()
示例3 - colorchooser 颜色选择对话框
colorchooser 颜色选择对话框
提供了让用户选择需要的颜色
返回值,
- 用户选择颜色:返回一个二元组:(RGB, 十六进制)
- 用户点取消:(None, None)
# colorchooser 颜色选择对话框
from tkinter import *
from tkinter import colorchooser
root = Tk()
def callback():
fileName = colorchooser.askcolor()
print(fileName)
Button(root, text="选择颜色", command=callback).pack()
mainloop()
🔰pygame(OLD:P79~P97)
P79 Pygame:初次见面,请大家多多关照
- pygame官网:
- 安装
- pip install pygame
- 检测时候安装成功:
import pygame
print(pygame.ver)- 功能
- 绘制图形
- 显示图片
- 动画效果
- 键鼠交互
- 声音特效
- 碰撞检测
示例1 - 小甲鱼到处跑
# 小甲鱼到处跑 示例
import pygame
import sys
# 初始化Pygame模块
pygame.init()
size = width, height = 900, 600
speed = [-2, 1]
bg = (255, 255, 255)
# 创建指定大小的窗口
screen = pygame.display.set_mode(size) # 返回surface对象
# 设置窗口标题
pygame.display.set_caption("初次见面,请大家多多关照!")
# 加载图片
turtle = pygame.image.load(r"image/turtle.png") # pygame支持多种图片格式;返回surface对象
# 获得图像的位置矩形
position = turtle.get_rect()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# 移动图像
position = position.move(speed)
if position.left < 0 or position.right > width:
# 翻转图像
turtle = pygame.transform.flip(turtle, True, False)
# 反方向移动
speed[0] = -speed[0]
if position.top < 0 or position.bottom > height:
speed[1] = -speed[1]
# 填充背景
screen.fill(bg)
# 更新图像
screen.blit(turtle, position)
# 更新界面
pygame.display.flip()
# 延迟10毫秒
pygame.time.delay(10)
P80 Pygame:解惑
- surface对象
- 用来表示图像的对象
- 将一个图像绘制到另一个图像上
- 原理是移动图片上的像素颜色
- 图像移动
- 例如 小甲鱼跑来跑去
- 实现方法:
- 获取图像位置的矩形范围
position = turtle.get_rect()
- 调用move方法,修改矩形范围的位置
position = position.move(speed)
- 填充白色背景
screen.fill(bg)
- 重新填充新的位置的图像
screen.blit(turtle, position)
- 更新页面
pygame.display.flip()
- 帧率设置
- 可以达到演示效果
- 帧率不高于200帧
clock = pygame.time.Clock() # 实例化clock对象
clock.tick(200)
- 帮助文档
示例1
import pygame
import sys
pygame.init()
size = width, height = 600, 400
screen = pygame.display.set_mode(size)
pygame.display.set_caption("FishC Demo")
f = open("record.txt", 'w')
while True:
for event in pygame.event.get():
f.write(str(event) + '\n')
if event.type == pygame.QUIT:
f.close()
sys.exit()
P81 Pygame:事件
示例 - 将产生的事件记录保存在文件中
1将产生的事件记录保存在文件中
import pygame
import sys
pygame.init()
size = width, height = 600, 400
screen = pygame.display.set_mode(size)
pygame.display.set_caption("初次见面,请大家多多关照!")
f = open("record.txt", 'w')
while True:
for event in pygame.event.get():
f.write(str(event) + '\n')
if event.type == pygame.QUIT:
f.close()
sys.exit()
示例 - 将产生的事件显示在窗口内
原理:
由于pycharme不能直接显示文字
因此,先将文字渲染成图片,在以图片形式显示,并不断刷新
pagame事件对照表如图:
import pygame
import sys
pygame.init()
size = width, height = 600, 800
bg = (0, 0, 0)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("FishC Demo")
event_texts = []
# 要在Pygame中使用文本,必须创建Font对象
# 第一个参数指定字体,第二个参数指定字体的尺寸
font = pygame.font.Font(None, 20) # 实例化font对象
# 调用get_linesize()方法获得每行文本的高度
line_height = font.get_linesize()
position = 0
screen.fill(bg)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
# render()方法将文本渲染成Surface对象
# 第一个参数是待渲染的文本
# 第二个参数指定是否消除锯齿
# 第三个参数指定文本的颜色
screen.blit(font.render(str(event), True, (0, 255, 0)), (0, position))
position += line_height
if position > height:
# 满屏时清屏
position = 0
screen.fill(bg)
pygame.display.flip()
示例 - 方向键控制小乌龟
方向键控制小乌龟
import pygame
import sys
from pygame.locals import * # 将 pygame 的所有常量名导入
# 初始化Pygame
pygame.init()
size = width, height = 600, 400
bg = (255, 255, 255)
speed = [0, 0]
clock = pygame.time.Clock()
screen = pygame.display.set_mode(size)
pygame.display.set_caption("初次见面,请大家多多关照!")
turtle = pygame.image.load(r"image/turtle.png")
position = turtle.get_rect()
# 指定龟头的左右朝向
l_head = turtle
r_head = pygame.transform.flip(turtle, True, False)
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
if event.type == KEYDOWN:
if event.key == K_LEFT: # 按下键盘左键
speed = [-1, 0]
turtle = l_head
if event.key == K_RIGHT: # 按下键盘右键
speed = [1, 0]
turtle = r_head
if event.key == K_UP: # 按下键盘上键
speed = [0, -1]
if event.key == K_DOWN: # 按下键盘下键
speed = [0, 1]
position = position.move(speed)
if position.left < 0 or position.right > width:
# 翻转图像
turtle = pygame.transform.flip(turtle, True, False)
# 反方向移动
speed[0] = -speed[0]
if position.top < 0 or position.bottom > height:
speed[1] = -speed[1]
screen.fill(bg)
screen.blit(turtle, position)
pygame.display.flip()
clock.tick(30)
P82 Pygame:提高游戏颜值1
示例 - 界面设置
界面设置
set_mode(resolution=(0,0),flags=0,depth=0) -> Surface
- resolution参数:设置界面大小;
- flags此参数:扩展选项
- FULLSCREEN:全屏,需要关联退出全屏的快捷键!
- DOUBLEBUF:双缓冲
- HWSURFACCE:硬件加速支持,仅在犬奴平模式下可用
- OPENGL:使用OpenGL渲染
- RESIZABLE:使得窗口大小可调整
- NOFRAME:使得窗口没有边框和控制按钮
- depth参数:指定颜色位数。
- 默认情况下自动处理,不用设置它
- 32位、64位
获取系统支持的分辨率
ipmort pygame
pagame.init()
pygame.display.list_modes()
import pygame
import sys
# 将 pygame 的所有常量名导入
from pygame.locals import *
# 初始化Pygame
pygame.init()
fullscreen = False # 全屏标志
size = width, height = 600, 400
bg = (255, 255, 255)
speed = [0, 0]
clock = pygame.time.Clock()
screen = pygame.display.set_mode(size)
pygame.display.set_caption("初次见面,请大家多多关照!")
turtle = pygame.image.load(r"image/turtle.png")
position = turtle.get_rect()
# 指定龟头的左右朝向
l_head = turtle
r_head = pygame.transform.flip(turtle, True, False)
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
if event.type == KEYDOWN:
if event.key == K_LEFT:
speed = [-1, 0]
turtle = l_head
if event.key == K_RIGHT:
speed = [1, 0]
turtle = r_head
if event.key == K_UP:
speed = [0, -1]
if event.key == K_DOWN:
speed = [0, 1]
# 全屏(F11)快捷键
if event.key == K_F11:
fullscreen = not fullscreen
if fullscreen:
screen = pygame.display.set_mode((1920, 1080), FULLSCREEN | HWSURFACE)
else:
screen = pygame.display.set_mode(size)
position = position.move(speed)
if position.left < 0 or position.right > width:
# 翻转图像
turtle = pygame.transform.flip(turtle, True, False)
# 反方向移动
speed[0] = -speed[0]
if position.top < 0 or position.bottom > height:
speed[1] = -speed[1]
screen.fill(bg)
screen.blit(turtle, position)
pygame.display.flip()
clock.tick(30)
示例 - 让界面支持拖拽
让界面支持拖拽
- 首先设置 RESIZABLE 参数:使得窗口大小可调整
- 第二 检测事件:event.type == VIDEORESIZE
import pygame
import sys
# 将 pygame 的所有常量名导入
from pygame.locals import *
# 初始化Pygame
pygame.init()
fullscreen = False
size = width, height = 600, 400
bg = (255, 255, 255)
speed = [0, 0]
clock = pygame.time.Clock()
screen = pygame.display.set_mode(size, RESIZABLE)
pygame.display.set_caption("初次见面,请大家多多关照!")
turtle = pygame.image.load(r"image/turtle.png")
position = turtle.get_rect()
# 指定龟头的左右朝向
l_head = turtle
r_head = pygame.transform.flip(turtle, True, False)
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
if event.type == KEYDOWN:
if event.key == K_LEFT:
speed = [-1, 0]
turtle = l_head
if event.key == K_RIGHT:
speed = [1, 0]
turtle = r_head
if event.key == K_UP:
speed = [0, -1]
if event.key == K_DOWN:
speed = [0, 1]
# 全屏(F11)
if event.key == K_F11:
fullscreen = not fullscreen
if fullscreen:
screen = pygame.display.set_mode((1920, 1080), FULLSCREEN | HWSURFACE)
else:
screen = pygame.display.set_mode(size)
# 用户调整窗口尺寸
if event.type == VIDEORESIZE:
size = event.size
width, height = size
print(size)
screen = pygame.display.set_mode(size, RESIZABLE)
position = position.move(speed)
if position.left < 0 or position.right > width:
# 翻转图像
turtle = pygame.transform.flip(turtle, True, False)
# 反方向移动
speed[0] = -speed[0]
if position.top < 0 or position.bottom > height:
speed[1] = -speed[1]
screen.fill(bg)
screen.blit(turtle, position)
pygame.display.flip()
clock.tick(30)
示例 - 使得图像可以缩放
使得图像可以缩放
图像变换的方法:
- filp 上下、左右翻转图像
- scale 缩放图像(快速)
- rotate 旋转图像
- rotozoom 缩放并旋转图像
- scale2x 快速放大一倍图像
- smoothscale 平滑缩放图像(精准)
- chop 剪裁图像
import pygame
import sys
# 将 pygame 的所有常量名导入
from pygame.locals import *
# 初始化Pygame
pygame.init()
fullscreen = False
size = width, height = 600, 400
bg = (255, 255, 255)
speed = [0, 0]
clock = pygame.time.Clock()
screen = pygame.display.set_mode(size, RESIZABLE)
pygame.display.set_caption("初次见面,请大家多多关照!")
# 设置放大缩小比率
ratio = 1.0
oturtle = pygame.image.load(r"image/turtle.png")
turtle = oturtle
oturtle_rect = oturtle.get_rect()
position = turtle_rect = oturtle_rect
# 指定龟头的左右朝向
l_head = turtle
r_head = pygame.transform.flip(turtle, True, False)
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
if event.type == KEYDOWN:
if event.key == K_LEFT:
speed = [-1, 0]
turtle = l_head
if event.key == K_RIGHT:
speed = [1, 0]
turtle = r_head
if event.key == K_UP:
speed = [0, -1]
if event.key == K_DOWN:
speed = [0, 1]
# 全屏(F11)
if event.key == K_F11:
fullscreen = not fullscreen
if fullscreen:
screen = pygame.display.set_mode((1920, 1080), FULLSCREEN | HWSURFACE)
else:
screen = pygame.display.set_mode(size)
# 放大、缩小小乌龟(=、-),空格恢复原始尺寸
if event.key == K_EQUALS or event.key == K_MINUS or event.key == K_SPACE:
# 最大只能放大一倍,缩小50%
if event.key == K_EQUALS and ratio < 2:
ratio += 0.1
if event.key == K_MINUS and ratio > 0.5:
ratio -= 0.1
if event.key == K_SPACE:
ratio = 1
turtle = pygame.transform.smoothscale(oturtle, (
int(oturtle_rect.width * ratio), int(oturtle_rect.height * ratio)))
# 相应修改龟头两个朝向的Surface对象,否则一点击移动就打回原形
l_head = turtle
r_head = pygame.transform.flip(turtle, True, False)
# 获得小乌龟缩放后的新尺寸
turtle_rect = turtle.get_rect()
position.width, position.height = turtle_rect.width, turtle_rect.height
# 用户调整窗口尺寸
if event.type == VIDEORESIZE:
size = event.size
width, height = size
print(size)
screen = pygame.display.set_mode(size, RESIZABLE)
position = position.move(speed)
if position.left < 0 or position.right > width:
# 翻转图像
turtle = pygame.transform.flip(turtle, True, False)
# 反方向移动
speed[0] = -speed[0]
if position.top < 0 or position.bottom > height:
speed[1] = -speed[1]
screen.fill(bg)
screen.blit(turtle, position)
pygame.display.flip()
clock.tick(30)
示例 - 使用- rotate 旋转图像 实现贴边爬行
使用- rotate 旋转图像 实现贴边爬行
import pygame
import sys
from pygame.locals import *
pygame.init()
fullscreen = False
size = width, height = 640, 480
bg = (255, 255, 255)
clock = pygame.time.Clock()
screen = pygame.display.set_mode(size)
pygame.display.set_caption("FishC Demo")
turtle = pygame.image.load(r"image/turtle.png")
position = turtle_rect = turtle.get_rect()
# 小乌龟顺时针行走
speed = [5, 0]
turtle_right = pygame.transform.rotate(turtle, 90) # 旋转90度
turtle_top = pygame.transform.rotate(turtle, 180)
turtle_left = pygame.transform.rotate(turtle, 270)
turtle_bottom = turtle
# 刚开始走顶部
turtle = turtle_top
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
# 全屏(F11)
if event.type == KEYDOWN:
if event.key == K_F11:
fullscreen = not fullscreen
if fullscreen:
screen = pygame.display.set_mode((1920, 1080), FULLSCREEN | HWSURFACE)
else:
screen = pygame.display.set_mode(size)
# 移动图像
position = position.move(speed)
if position.right > width:
turtle = turtle_right
# 变换后矩形的尺寸发生改变
position = turtle_rect = turtle.get_rect()
# 矩形尺寸的改变导致位置也有变化
position.left = width - turtle_rect.width
speed = [0, 5]
if position.bottom > height:
turtle = turtle_bottom
position = turtle_rect = turtle.get_rect()
position.left = width - turtle_rect.width
position.top = height - turtle_rect.height
speed = [-5, 0]
if position.left < 0:
turtle = turtle_left
position = turtle_rect = turtle.get_rect()
position.top = height - turtle_rect.height
speed = [0, -5]
if position.top < 0:
turtle = turtle_top
position = turtle_rect = turtle.get_rect()
speed = [5, 0]
screen.fill(bg)
screen.blit(turtle, position)
pygame.display.flip()
clock.tick(30)
P83 Pygame:提高游戏颜值2
示例 - 实现裁剪
实现裁剪
- rect方法
- rect(Surface, color, Rect, width=0)
- Surface参数
- color参数
- Rect参数
- width=0
import pygame
import sys
from pygame.locals import *
pygame.init()
size = width, height = 800, 600
bg = (255, 255, 255)
clock = pygame.time.Clock()
screen = pygame.display.set_mode(size)
pygame.display.set_caption("FishC Demo")
turtle = pygame.image.load(r"image/turtle.png")
# 0 -> 未选择,1 -> 选择中,2 -> 完成选择
select = 0
select_rect = pygame.Rect(0, 0, 0, 0)
# 0 -> 未拖拽,1 -> 拖拽中,2 -> 完成拖拽
drag = 0
position = turtle.get_rect()
position.center = width // 2, height // 2
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
elif event.type == MOUSEBUTTONDOWN:
if event.button == 1:
# 第一次点击,选择范围
if select == 0 and drag == 0:
pos_start = event.pos
select = 1
# 第二次点击,推拽图像
elif select == 2 and drag == 0:
capture = screen.subsurface(select_rect).copy()
cap_rect = capture.get_rect()
drag = 1
# 第三次点击,初始化
elif select == 2 and drag == 2:
select = 0
drag = 0
elif event.type == MOUSEBUTTONUP:
if event.button == 1:
# 第一次释放,结束选择
if select == 1 and drag == 0:
pos_stop = event.pos
select = 2
# 第二次释放,结束拖拽
if select == 2 and drag == 1:
drag = 2
screen.fill(bg)
screen.blit(turtle, position)
# 实时绘制选择框
if select:
mouse_pos = pygame.mouse.get_pos()
if select == 1:
pos_stop = mouse_pos
select_rect.left, select_rect.top = pos_start
select_rect.width, select_rect.height = pos_stop[0] - pos_start[0], pos_stop[1] - pos_start[1]
pygame.draw.rect(screen, (0, 0, 0), select_rect,1)
# 拖拽裁剪的图像
if drag:
if drag == 1:
cap_rect.center = mouse_pos
screen.blit(capture, cap_rect)
pygame.display.flip()
clock.tick(30)
P84 Pygame:提高游戏颜值3
示例 - 高效载入图片的方法
高效载入图片的方法 调用.convert()方法转换
- bg = pygame.image.load(“bg.jpg”).convert()
- 转换指的是像素格式的转换
- bg = pygame.image.load(“bg.jpg”).convert_alpha()
图片格式
- png 无损压缩
- jpg 不支持透明,载入时用.convert()
- png gif支持透明,载入时用.convert_alpha()
import pygame
import sys
from pygame.locals import *
pygame.init()
size = width, height = 640, 480
bg = (0, 0, 0)
clock = pygame.time.Clock()
screen = pygame.display.set_mode(size)
pygame.display.set_caption("FishC Demo")
turtle = pygame.image.load("./小甲鱼源代码/p18/image/turtle.jpg").convert()
background = pygame.image.load("./小甲鱼源代码/p18/image/background.jpg").convert()
position = turtle.get_rect()
position.center = width // 2, height // 2
turtle.set_colorkey((255, 255, 255))
turtle.set_alpha(200)
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
screen.blit(background, (0, 0))
screen.blit(turtle, position)
pygame.display.flip()
clock.tick(30)
示例 - 将白色区域设置成透明
# 将白色区域设置成透明
import pygame
import sys
from pygame.locals import *
pygame.init()
size = width, height = 640, 480
bg = (0, 0, 0)
clock = pygame.time.Clock()
screen = pygame.display.set_mode(size)
pygame.display.set_caption("FishC Demo")
turtle = pygame.image.load("./小甲鱼源代码/p18/image/turtle.png").convert_alpha()
background = pygame.image.load("./小甲鱼源代码/p18/image/background.jpg").convert()
position = turtle.get_rect()
position.center = width // 2, height // 2
# 将白色区域设置成透明
for i in range(position.width):
for j in range(position.height):
temp = turtle.get_at((i, j))
if temp[3] != 0:
temp[3] = 200
turtle.set_at((i, j), temp)
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
screen.blit(background, (0, 0))
screen.blit(turtle, position)
pygame.display.flip()
clock.tick(30)
示例
import pygame
import sys
from pygame.locals import *
pygame.init()
size = width, height = 640, 480
bg = (0, 0, 0)
clock = pygame.time.Clock()
screen = pygame.display.set_mode(size)
pygame.display.set_caption("FishC Demo")
turtle = pygame.image.load("./小甲鱼源代码/p18/image/turtle.png").convert_alpha()
background = pygame.image.load("./小甲鱼源代码/p18/image/background.jpg").convert()
position = turtle.get_rect()
position.center = width // 2, height // 2
def blit_alpha(target, source, location, opacity):
x = location[0]
y = location[1]
temp = pygame.Surface((source.get_width(), source.get_height())).convert()
temp.blit(target, (-x, -y))
temp.blit(source, (0, 0))
temp.set_alpha(opacity)
target.blit(temp, location)
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
screen.blit(background, (0, 0))
blit_alpha(screen, turtle, position, 200)
pygame.display.flip()
clock.tick(30)
P85 Pygame:基本图像绘制
示例 - rect方法绘制矩形
- rect方法绘制矩形
- rect(Surface, color, Rect, width=0)
- Surface参数:绘制在surface对象上
- color参数:矩形颜色
- Rect参数:矩形范围
- width=0:边框大小。0表示填充矩形,1以上表示 使用color指定的颜色绘制边框
import pygame
import sys
from pygame.locals import *
pygame.init()
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
size = width, height = 640, 480
screen = pygame.display.set_mode(size)
pygame.display.set_caption("FishC Demo")
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
screen.fill(WHITE)
pygame.draw.rect(screen, BLACK, (50, 50, 150, 50), 0)
pygame.draw.rect(screen, BLACK, (250, 50, 150, 50), 1)
pygame.draw.rect(screen, BLACK, (450, 50, 150, 50), 10)
pygame.display.flip() # 把内存中的画面反转到窗口中 <- 双缓冲关系
clock.tick(10)
示例 - polygon方法绘制多边形
- polygon方法绘制多边形
- polygon(Surface, color, pointlist, width=0)
- Surface参数:绘制在surface对象上
- color参数:颜色
- pointlist参数:多边形范围
- width=0:边框大小。0表示填充矩形,1以上表示 使用color指定的颜色绘制边框
import pygame
import sys
from pygame.locals import *
pygame.init()
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
points = [(200, 75), (300, 25), (400, 75), (450, 25), (450, 125), (400, 75), (300, 125)]
size = width, height = 640, 200
screen = pygame.display.set_mode(size)
pygame.display.set_caption("FishC Demo")
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
screen.fill(WHITE)
pygame.draw.polygon(screen, GREEN, points, 0)
pygame.display.flip()
clock.tick(10)
示例 - circle方法绘制圆形
- circle方法绘制圆形
- circle(Surface, color, pos, radius, width=0)
- Surface参数:绘制在surface对象上
- color参数:颜色
- pos参数:圆心位置
- radius参数:半径大小
- width=0:边框大小。0表示填充矩形,1以上表示 使用color指定的颜色绘制边框
# 跟随鼠标的圆形
import pygame
import sys
from pygame.locals import *
pygame.init()
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
size = width, height = 640, 480
screen = pygame.display.set_mode(size)
pygame.display.set_caption("FishC Demo")
position = size[0] // 2, size[1] // 2
moving = False
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
if event.type == MOUSEBUTTONDOWN:
if event.button == 1:
moving = True
if event.type == MOUSEBUTTONUP:
if event.button == 1:
moving = False
if moving:
position = pygame.mouse.get_pos()
screen.fill(WHITE)
pygame.draw.circle(screen, RED, position, 25, 1)
pygame.draw.circle(screen, GREEN, position, 75, 1)
pygame.draw.circle(screen, BLUE, position, 125, 1)
pygame.display.flip()
clock.tick(120)
示例 - ellipse方法绘制椭圆
- ellipse方法绘制椭圆
- ellipse(Surface, color, Rect, width=0)
- Surface参数:绘制在surface对象上
- color参数:颜色
- Rect参数:限定矩形
- width=0:边框大小。0表示填充矩形,1以上表示 使用color指定的颜色绘制边框
import pygame
import sys
from pygame.locals import *
pygame.init()
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
size = width, height = 640, 300
screen = pygame.display.set_mode(size)
pygame.display.set_caption("FishC Demo")
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
screen.fill(WHITE)
pygame.draw.ellipse(screen, BLACK, (100, 100, 440, 100), 1)
pygame.draw.ellipse(screen, BLACK, (220, 50, 200, 200), 1)
pygame.display.flip()
clock.tick(120)
示例 - arc方法绘制弧线
- arc方法绘制弧线
- arc(Surface, color, Rect, start_angle, stop_angle, width=1)
- Surface参数:绘制在surface对象上
- color参数:颜色
- Rect参数:限定矩形
- start_angle参数:起始角度
- stop_angle参数:结束角度
- width=0:边框大小。0表示填充矩形,1以上表示 使用color指定的颜色绘制边框
import pygame
import sys
import math
from pygame.locals import *
pygame.init()
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
size = width, height = 640, 300
screen = pygame.display.set_mode(size)
pygame.display.set_caption("FishC Demo")
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
screen.fill(WHITE)
pygame.draw.arc(screen, BLACK, (100, 100, 440, 100), 0, math.pi, 1)
pygame.draw.arc(screen, BLACK, (220, 50, 200, 200), math.pi, math.pi * 2, 1)
pygame.display.flip()
clock.tick(120)
示例 - line、lines、aaline、aalines方法绘制线条
- line、lines、aaline、aalines方法绘制线条
- line(Surface, color, start_pos, end_pos, width=1)
- lines(Surface, color, closed, pointlist, width=1)
- closed参数:=1则首尾相连
- aaline(Surface, color, startpos, endpos, blend=1)
- aalines(Surface, color, closed, pointlist, blend=1)
import pygame
import sys
from pygame.locals import *
pygame.init()
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
points = [(200, 75), (300, 25), (400, 75), (450, 25), (450, 125), (400, 75), (300, 125)]
size = width, height = 640, 480
screen = pygame.display.set_mode(size)
pygame.display.set_caption("FishC Demo")
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
screen.fill(WHITE)
pygame.draw.lines(screen, GREEN, 1, points, 1)
pygame.draw.line(screen, BLACK, (100, 200), (540, 250), 1)
pygame.draw.aaline(screen, BLACK, (100, 250), (540, 300), 1)
pygame.draw.aaline(screen, BLACK, (100, 300), (540, 350), 0)
pygame.display.flip()
clock.tick(10)
了解的差不多了,以下内容暂不学习了,估计也用不到
P86 Pygame:动画精灵
1
P87 Pygame:碰撞检测
1
P88 Pygame:播放声音和音效
1
P89 Pygame:摩擦摩擦
1
P90 Pygame:游戏胜利
1
P91 Pygame:飞机大战1
1
P92 Pygame:飞机大战2
1
P93 Pygame:飞机大战3
1
P94 Pygame:飞机大战4
1
P95 Pygame:飞机大战5
1
P96 Pygame:飞机大战
1
P97 Pygame:飞机大战
1
更多推荐
所有评论(0)