(考研成功的第一个暑假,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
  • 新手注意点:

🔰变量、字串符、运算符(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
跳出一层循环

随机数模块

BIF速查宝典

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__) 查看所有BIF
  • help(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)

流程图+思维导图

思维导图怎么画
  • 方法:
    • 顶层设计,逐层往下分析。
    • 按功能分析
    • 按元素分析
  • 比如一个小游戏包含哪些元素,这些元素要干什么,怎么样才能实现指定的功能
其他

🔰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(变量)
列表推导式
  • [express for target in iterable]
  • [express for target in iterable if 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")
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
  • 判断元素是否包含在某个序列
    • in :如"i" in "ivcc"
    • not in
  • del
    • 用于删除一个或多个指定语句、对象
      • 如:x = “ivcc”; del x, y
    • 用于删除可变序列中的指定元素
      • 如:x = [1, 2, 3, 4, 5]
        • del x[1:4] ->相当于切片中的 x[1:4] = []
  • 列表、元组、字符串相互转换
    • list() 转换为列表
    • tuple() 转换为元组
    • str() 转换为字符串
  • 函数
    • 对比传入参数返回最值
      • min() 返回列表中最小元素、字符串中字母编码值
      • max()
        • min(s, defaule="空的序列"),若序列可能为空,用这种方法
    • **
      • len() 计算长度
      • sum() 计算求和
        • start参数 指定求和起始值,从起始值开始加 sum(s, start=100)
    • **
      • sorted() 从小到大排序,返回全新列表 (注意:.sort()方法会改变原列表)
        • sorted(t,reverse=true)
        • sorted(t, key=len) key可以干预配许算法
          • len 比较每个元素长度
      • reversed() 翻转

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参数:映射类型
内建函数
  • 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()
      • 特点:拷贝出一个新的字典
  • dict2 = dict1 拷贝
    • 两个字典指向同一个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()

测试文本
file内容分割test_file.txt

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标准异常

表 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浮点计算错误
GeneratorExitgenerator.close()方法被调用的时候
ImportError导入模块失败的时候
IndexError索引超出序列的范围
KeyError字典中查找一个不存在的关键字
KeyboardInterrupt用户输入中断键(Ctrl+c)
MemoryError内存溢出(可通过删除对象释放内存)
NameError尝试访问一个不存在的变量
NotImplementedError尚未实现的方法
OSError操作系统产生的异常(例如打开一个不存在的文件)
OverflowError数值运算超出最大限制
ReferenceError弱引用(weak reference)试图访问一个已经被垃圾回收机制回收了的对象
RuntimeError一般的运行时错误
StopIteration迭代器没有更多的值
SyntaxErrorPython的语法错误
IndentationError缩进错误
TabErrorTab和空格混合使用
SystemErrorPython编译器系统错误
SystemExitPython编译器进程被关闭
TypeError不同类型间的无效操作
UnboundLocalError访问一个未初始化的本地变量(NameError的子类)
UnicodeErrorUnicode相关的错误(ValueError的子类)
UnicodeEncodeErrorUnicode编码时的错误(UnicodeError的子类)
UnicodeDecodeErrorUnicode解码时的错误(UnicodeError的子类)
UnicodeTranslateErrorUnicode转换时的错误(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
      • SystemError
      • TypeError
      • ValueError
        • UnicodeError
          • UnicodeDecodeError
          • UnicodeEncodeError
          • UnicodeTranslateError
      • 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语句

参考python之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中。只需知道名字进行调用即可,保密性更高
  • 继承:子类能够自动获取父类的数据和方法
  • 多态:同一方法可根据发送对象的不同而采用不同的响应
    • 一个对象的实际类型是确定的,但是指向对象的引用类型有很多

类的一个创建和调用、继承、多态的例子:

# 对象的属性、方法调用举例
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.xdd.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)

可参考:Python 魔法方法详解__fishC

  • 总是被双下划线包围,如__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


# __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
  • 只能使用提供的有限资源完成

需要使用的资源:

重写 strrepr,示例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 属性访问

四种访问方式

  1. 直接访问:对象.属性
  2. getatter()访问:getatter(对象, '属性名', '没有此属性!')
  3. property 通过属性操作属性:示例1
  4. 魔法方法的重写
# 前三种方法访问属性,示例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)
'''

小甲鱼零基础入门学习python笔记

🔰生成器(OLD:P50)

P50乱入,生成器

协同程序(生成器的应用)

所谓协同程序,就是可以运行的独立函数调用,函数可以暂停或者挂起,并在需要的时候从程序离开的地方继续或者重新开始。

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)

用于解决模块太多而杂乱无章、并且可能出现命名冲突的问题
创建

  1. 创建一个文件夹,用于存放模块,文件夹的名字即包的名字;
  2. 在文件夹中创建一个__init__.py的模块文件,内容可以为空,用于告诉python要将这个文件夹当成一个包;
  3. 将相关的模块放入文件夹中。

调用
import 包名.模块名
然后,参考模块的导入与调用即可,同样的方法,有三种方法

P53 像一个极客去思考

使用现成的模块,如:python标准库中的模块
python标准库中的模块数百个,怎么自己去探索模块?如下:
查看pyhon自带文档:IDLE -> Help ->Python Docs F1

目录中的英文包含的内容
What’s New in Pythonpython新的特性
The Python Tutorial简易的教程
介绍了语法
Installing Python Modules安装python的第三方模块
Distributing Python Modules发布第三方模块
pypi社区,全世界分享的模块:https://pypi.org/
The Python Language Referencepython语法、设计哲学
Python Setup and Usage在不同平台使用
Python HOWTOs详细深入探讨主题
Extending and Embedding the Python Interpreter使用C/C++开发python扩展模块
Python/C API Reference Manual扩展模块API函数
PEPpython增强建议书
用来规范python开发
PEP0: PEP索引:https://www.python.org/dev/peps/

文档搜索:
在文档点索引,或搜索,如搜索timeit
其中:
第一段时介绍模块
Basic Examples:基础例程
Examples:详细运用的例子

快速掌握模块的用法
  1. 先导入模块,然后调用__doc__属性,查看模块简介。如import timeit print(timeit.__doc__)
  2. 使用dir()可以查询到该模块定义了哪些变量、函数和类。如 dir(timeit)
    • 其中的__all__属性:timeit.__all__可以给我们提供可供外界调用的东西
    • 其中的__file__属性:timeit.__file__可以给我们提供模块源代码所在位置
      • 阅读源代码可以快速提升能力哦!
  3. 使用help(timeit) 帮助文档,比print(timeit.__doc__)调出来的简单一点

🔰爬虫

P54 论一只爬虫的自我修养

python如何访问互联网?

  • url + lib = urllib

    • url就是网址,lib就是网址首页
    • urllib是一个包,包含了4个模块:
      • urllib.request for opening and reading URLs
      • urllib.error containing the exceptions raised by urllib.request
      • urllib.parse for parsing URLs
      • urllib.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
    • 步骤:
        1. 参数是一个字典 {‘类型’:‘代理ip:端口号’}
        • proxy_support = urllib.request.ProxyHandler({})
        1. 定制、创建一个 opener
        • opene用于打开网页,可以给它加上特殊的headers、指定相关的代理等
        • opener = urllib.request.build_opener(proxy_support)
        1. 安装 opener
        • 安装后,以后调用方便
        • urllib.request.install_opener(opener)
        1. 调用 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()

技巧总结:

  1. 编写主函数,分析所需子函数:
  2. 搭建好子函数框架
  3. 编写子函数
  4. 调试运行

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')

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),参数代表子组索引。没有参数默认提取所有内容
        • 返回匹配的起始位置及范围
          • .start()
          • .end()
          • .span() 范围
      • 编译后的正则表达式模式对象:regex = re.compile(),regex.secrch(string[, pos[, endpos]])
        • string[参数:要搜索的字符串
        • pos[可选参数:要搜索的起始位置
        • endpos]]可选参数:要搜索的结束位置
          • 示例:rx.rearch(string, 0, 50) 等价于 rx.rearch(string[:50], 0)
  • findall()方法
    • 功能
      • 返回匹配到的内容
    • 调用
      • re.findall(pattern, string, flags=0)
    • 如果正则表达式包含子组,将会把子组内容单独返回;如果存在多个子组,将会将匹配内容组合成元组再返回
      • 如示例中的p = r’<img class=“BDE_Image” src="([^"]+.jpg)’ imglist = re.findall(p, html)
        • 其中的src="([^"]+.jpg中的括号就是子组
    • 如何不捕获子组?
      • (?:…) 非捕获组,即该子组匹配的字符串无法从后面获取

下载贴吧中的图片,示例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
  • 两种常用的写法
    • 示例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所需要的前提和步骤
  1. python
    1. 大前提,也可以加上pycharme。
  2. pywin32 可提供一个像是PC一样的IDE
    1. pip install pywin32
  3. pip install lxml
  4. pip install py``OpenSSL
  5. pip install Scrapy

P64 论11:scrapy

使用Scrapy抓取一个网站的四个步骤
  • 创建Scrapy项目
  • 定义Item容器
  • 编写爬虫
  • 存储内容

Scrapy框架结构

scrapy架构图.png
绿色线条代表数据流

  • 各个组件介绍
    • 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()

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组件。
  • 在组件上安装垂直滚动条:
    1. 设置该组件的yscrollbarcommand选项为Scrollbar组件的set()方法
    2. 设置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

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐