【python教程】– 入门 | 小甲鱼《零基础入门学Python》教程笔记(知识点详细、源码可复制)全

(考研成功的第一个暑假,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)

    python的应用

  • 人工智能、数据分析、科学计算、网络爬虫、自动化开发、自动化部署
  • python优点

  • 强大、快速、兼容性强、入门轻松、开源
  • python的后台——pypl

  • 第三方模块
  • 课程参考书籍

  • 零基础入门学习Python(第2版)
  • python相关官网

  • python官网
  • 鱼C工作室官网
  • 编辑器

  • IDLE
  • 推荐使用,新手从基础学起
  • 交互模式 + 编辑器模式
  • Hello Word

  • print(“Hello Word”)
  • IDLE的两种模式:交互模式、编辑器模式
  • 示例

    """ 用python设计第一个游戏 """
    
    temp = input("猜一个数字:")
    guess = int(temp)
    
    if guess == 8:
    	print("^_^")
      print("@_@")
    else:
    	print("*_*")
      
    print("结束,不玩了")
    
  • 设置IDLE字体

  • 设置 -> Options -> Configure IDLE -> (推荐使用)Consolas
  • 新手注意点:

  • 英文标点
  • 正确的缩进
  • Python3代码样式指导手册(PEP8)
  • 函数的拼写(BIF内置函数,通过dir(builtins))
  • P4 变量和字串符(上)

  • 注意

  • 变量名不能以数字开头
  • 中文字符可以作为变量名
  • 字符串

  • 单引号
  • 双引号
  • P5 变量和字串符(下)

  • 转义字符 +符号 (可用于显示引号中的引号)
  • 反斜杠不能放在字符串末尾,使用 r 可以定义为原始字符 如print(r"D:\three\two\one")

  • 三单引号、三双引号 -> 此用法无需每行结尾输入\n\ 后才换行

  • 字符串的加减

  • 相加 -> 拼接
  • 相乘 -> 复制
  • P6 (比较运算符)是时候讲代码了

    转换

  • guess = int(temp)
  • 比较运算符

    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("游戏结束,不玩啦~~")
    

    P9-P10 数字类型

    整数

  • 有无限大的长度(无限大的精度)
  • 浮点数

  • 存储有误差
  • 精确存储浮点数
  • import decimal
    a = decimal.Decimal('0.2')
    

    复数

    x = 1 + 2j  //形式
    
    x.real  //获取实部
    x.imag	//获取虚部
    

    运算

    P11 布尔类型

    False是假
    空字符串""是假
    值等于零是假

    false =0 ;true=1

    逻辑运算符:

  • P12 短路逻辑和运算符优先级

    短路逻辑

  • 从左往右,只有当第一个操作数的值无法确定逻辑运算结果时,才对第二个操作数进行求值。(谁影响结果,就把谁扔出来 )
  • 运算符优先级

  • P13-P14 谋定而后动,知止而有得

    (2021-6-7)

    流程图+思维导图

    思维导图怎么画

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

  • Scratch Desktop(图像化编程软件)
  • 《零基础入门学习Scratch》(少儿编程)
  • 零基础入门学习web开发
  • (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, "是一个素数")
    

    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,但停更很久了,也不想等更新,太慢
    就先看老教程吧
    老教程包含了变量和字符串、数据类型、分支循环、列表、元组、字符串、序列,到此接着学


    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 /> 
    

    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))
  • 注意:此方法得到的集合是无序的
  • 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()
    

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

    异常层次结构

  • BaseException
  • SystemExit
  • KeyboardInterrupt
  • GeneratorExit
  • Exception
  • StopIteration
  • ArithmeticError
  • FloatingPointError
  • OverflowError
  • ZeroDivisionError
  • AssertionError
  • AttributeError
  • BufferError
  • EOFError
  • ImportError
  • LookupError
  • IndexError
  • KeyError
  • MemoryError
  • NameError
  • UnboundLocalError
  • OSError
  • BlockingIOError
  • ChildProcessError
  • ConnectionError
  • BrokenPipeError
  • ConnectionAbortedError
  • ConnectionRefusedError
  • ConnectionResetError
  • FileExistsError
  • FileNotFoundError
  • InterruptedError
  • IsADirectoryError
  • NotADirectoryError
  • PermissionError
  • ProcessLookupError
  • TimeoutError
  • ReferenceError
  • RuntimeError
  • NotImplementedError
  • SyntaxError
  • IndentationError
  • TabError
  • 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中。只需知道名字进行调用即可,保密性更高
  • 可参考:OO封装、继承、多态
  • 继承:子类能够自动获取父类的数据和方法
  • 多态:同一方法可根据发送对象的不同而采用不同的响应
  • 一个对象的实际类型是确定的,但是指向对象的引用类型有很多
  • 类的一个创建和调用、继承、多态的例子:

    # 对象的属性、方法调用举例
    class Cooldream:
        """关于类的一个简单例子"""
        # 属性
        interesting = '计算机编程、手工DIY'
        gender = '保密'
        telephone = '181********'
    
        # 方法
        def learnPython(self):
            print('先看完看完小甲鱼的教程...')
    
        def learnEnglish(self):
            print('先背单词吧...')
    
    # 继承举例,继承list
    class Myclass(Cooldream):
        pass
    
    # 多态举例
    class A:
        def fun(self):
            print('这里是A...')
    class B:
        def fun(self):
            print('这里是B...')
    
    if __name__ == '__main__':
        print('----对象的属性、方法调用举例----')
        # 调用对象的属性
        test = Cooldream()
        print('gender:', test.gender)
        # 调用对象中的方法
        test = Cooldream()
        test.learnPython()
    
        # 继承举例
        print('----继承举例,它可以使用现有Cooldream类的所有功能----')
        class1 = Myclass()
        print('继承来的gender:', class1.gender)
        print('继承来的learnPython:👇👇')
        class1.learnPython()
    
    
        # 多态举例
        print('----多态举例,调用的方法都是fun,但响应不同----')
        a = A()
        b = B()
    

    输出结果:

    ----对象的属性、方法调用举例----
    gender: 保密
    先看完看完小甲鱼的教程...
    ----继承举例,它可以使用现有Cooldream类的所有功能----
    继承来的gender: 保密
    继承来的learnPython👇👇
    先看完看完小甲鱼的教程...
    ----多态举例,调用的方法都是fun,但响应不同----
    这里是A...
    这里是B...
    

    相关名词解释:

  • 类(class):类=属性+方法,用来描述具有相同的属性和方法的对象的集合,类的内部了每个对象共有的属性和方法。
  • 属性:静态的特征
  • 方法:动态的动作,类中的函数
  • 对象:通过类的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
  • 实例化:创建一个类的实例,类的具体对象。
  • OOA:面向对象分享
    OOD:面向对象设计
    OOP:面向对象编程

    self的用法

    self 代表类的实例,self 在类的方法时是必须有的,要将self写进第一个参数,虽然在调用时不必传入相应的参数。
    例一

    # self使用举例1
    class Ball1:
        def setName(self, name):
            self.name = name
        def kick(self):
            print('我叫%s, 哎呀!谁踢我...' % self.name)
    
    if __name__ == '__main__':
        # self使用举例1
        print('----self使用举例1----')
        a = Ball1()
        a.setName('AA')
        b = Ball1()
        b.setName('BB')
        c = Ball1()
        c.setName('TT')
        a.kick()
        c.kick()
    
    ###运行结果
    ----self使用举例----
    我叫AA, 哎呀,谁踢我...
    我叫TT, 哎呀,谁踢我...
    

    例二、推荐方法,更简化

    # self使用举例2
    class Ball2:
        def __init__(self, name):
            self.name = name
        def kick(self):
            print('我叫%s, 哎呀!谁踢我...' % self.name)
    
    if __name__ == '__main__':
        # self使用举例2
        print('----self使用举例2----')
        a = Ball2('AA')
        b = Ball2('BB')
        c = Ball2('TT')
        a.kick()
        c.kick()
    
    ###运行结果
    ----self使用举例----
    我叫AA, 哎呀,谁踢我...
    我叫TT, 哎呀,谁踢我...
    
  • init(self,param1, param2, …)方法称为构造方法
  • 实例化一个对象时,此方法将会在对象创建时自动调用
  • 可以在实例化对象时传入参数,这些参数讲自动传入_init__方法中
  • 可通过重写_init__方法,来自初始化操作
  • 私有变量或私有函数

  • 在变量名或函数名前加上__
  • (访问方式一) 私有变量需通过内部函数访问,无法使用p.属性1进行访问,如示例中的“访问方式一”
  • (访问方式二) 但“私有”其实是“伪私有”,可通过p._类名__属性名进行外部访问,如示例中的“访问方式二”
  • 示例

    # 公有变量,即普通变量
    class TestG:
        attribute = 'attribute变量的值'
    
    
    # 私有变量
    class TestS:
        __attribute = '__attribute变量的值'
        def getAttribute(self):
            return self.__attribute
    
    if __name__ == '__main__':
        # 访问公有变量
        print('----访问公有变量----')
        p = TestG()
        print(p.attribute)
    
    
        # 访问私有变量
        print('----访问私有变量----')
        p = TestS()
        print('访问方式一:', p.getAttribute())
        print('访问方式二:', p._TestS__attribute)
    

    继承

    语法:class DrivedClassNName(BaseClassName):

  • DrivedClassNName是子类
  • BaseClassName是父类(基类、超类)
  • 子类可继承父类的所有属性和方法
  • 例子见上👆

  • 如果子类中父类同名的方法或属性,则会自动覆盖父类对应的方法或属性
  • 如果子类中的方法覆盖了父类中的方法,需要先引入 父类中的方法,有两种解决办法
  • 调用未绑定的父类方法
  • 父类名.父类中被覆盖的方法名(self)
  • super函数(更好的方案)
  • super().父类中被覆盖的方法名()
  • 不用传入self参数
  • 优点是,修改继承的父类时,不用修改引入代码,super()会自动查找
  • 多重继承

  • 语法:class DrivedClassNName(Base1, Base2, Base3, ...):
  • # 多重继承
    class Base1:
        def t1(self):
            print('我是t1')
    
    class Base2:
        def t2(self):
            print('我是t2')
    class Base3:
        def t3(self):
            print('我是t3')
    
    class C(Base1, Base2, Base3):
        pass
    
    
    if __name__ == '__main__':
        c = C()
        c.t1()
        c.t2()
        c.t3()
    
  • 缺点:
  • 容易造成代码混乱,非必须就不用
  • 拾遗

    拾遗_百度百科

    组合

  • 把类的实例化放到新类中,就把旧类组合一起了。
  • 优点:不需要多重继承,没有造成代码混乱的风险
  • 示例

    # 组合举例
    class Turtle:
        def __init__(self, x):
            self.num = x
    
    class Fish:
        def __init__(self, x):
            self.num = x
    
    class Pool:
        def __init__(self, x, y):
            self.turtle = Turtle(x)
            self.fish = Fish(y)
    
        def print_num(self):
            print('水池里一共有乌龟 %d 只, 小鱼 %d 条' % (self.turtle.num, self.fish.num))
    
    if __name__ == '__main__':
        pool = Pool(1, 10)
        pool.print_num()
    

    类、类对象和实例对象

    对实例对象的属性进行赋值时,将生成一个新的实例对象属性,去覆盖类对象的属性
    示例

    # 对实例对象的属性进行赋值时,将生成一个新的实例对象属性,去覆盖类对象的属性,示例
    class C1:
        count = 0
        
        
    if __name__ == '__main__':
        a = C1()
        b = C1()
        c = C1()
        print('----原始 a, b, c 的count属性:----\n', a.count, b.count, c.count)
        c.count += 10
        print('----把c 的实例化对象count属性+10 | a, b, c 的count属性:----\n', a.count, b.count, c.count)
        print('----类对象 C1 的count属性:\n', C1.count)
        C1.count += 100
        print('----把类对象 C1 的count属性+100 | a, b, c 的count属性:----\n', a.count, b.count, c.count)
        
    
    ----原始 a, b, c 的count属性:----
     0 0 0
    ----把c 的实例化对象count属性+10 | a, b, c 的count属性:----
     0 0 10
    ----类对象 C1 的count属性: 0
    ----把类对象 C1 的count属性+100 | a, b, c 的count属性:----
     100 100 10
    

  • 类中的属性时静态的,且类属性和类对象是相互绑定的,不会受实例对象属性的影响

  • 示例对象在对属性操作时,会创建新的实例对象属性,操作实例对象属性时,不影响类属性

  • 注意:

  • 如果属性名与方法名相同,属性会覆盖方法,如下示例
  • # 如果属性名与方法名相同,属性会覆盖方法,如下示例
    class C:
        def x(self):
            print('X-man')
            
    if __name__ == '__main__':
        c = C()
        print('---可调用方法 .x() ---')
        c.x()
        print('---增加属性名x,方法 .x() 被覆盖---')
        c.x = 1     # 创建一个实例对象c的一个属性,由于python变量不需声明,因此直接赋值就相当于定义了一个x属性
        print('c.x =', c.x)
        c.x()
        print('.x()被覆盖,不能调用了...')
            
    

    建议:

  • 如果属性和方法多而复杂,可使用 继承 和 组合 的方法扩展类
  • 属性名用名词
  • 方法名用动词
  • 绑定
    实例对象通过self与class中的方法绑定。没绑定的话,实例对象就不能调用class方法,举个没绑定的例子:
    示例一、没有self

    # 缺少self,将无法与实例对象绑定
    class BB:
        def printBB():        # 缺少self,将无法与实例对象绑定
            print('no zuo no die')
    
    if __name__ == '__main__':
        # 缺少self,将无法与实例对象绑定
        print('----类对象调用:----')
        BB.printBB()
        print('----实例对象调用:----')
        bb = BB()
        bb.printBB()
    

    示例二、有self

    # 有self,可绑定
    class CC:
        def setXY(self, x, y):
            self.x = x
            self.y = y
        def printXY(self):
            print(self.x, self.y)
            
            
    if __name__ == '__main__':
        # 有self,可绑定
        print('----------------------------------')
        dd = CC()
        print('dd属性>>', dd.__dict__)
        print('CC属性>>', CC.__dict__, '\n')
    
        print('----传入参数----')
        dd.setXY(4, 5)
        dd.printXY()
        print('dd属性>>', dd.__dict__)
        print('CC属性>>', CC.__dict__, '\n')
    
        print('----删除类CC,实例对象dd依然可调用CC中的方法,并且设置实例属性dd.x、dd.y ----')
        del CC
        dd.printXY()
        dd.setXY(5, 6)
        dd.printXY()
    

    注:
    通过 dd.dict 可查看对象属性,以字典形式返回,加上print才能在pycharm中打印出结果,IDE中无需加print

    在这里,self的一个作用是,将实例对象传入class方法,将他们绑定
    缺少self,就无法绑定,实例对象调用方法时将会报错

    dd.setXY(4, 5)相当于dd.setXY(dd, 4, 5), 默认将实例对象dd传入进方法,形成dd.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
  • 只能使用提供的有限资源完成
  • 需要使用的资源:

  • 使用time模块的localtime方法获取时间
  • 扩展阅读:time 模块详解(时间获取和转换)
  • time.localtime([secs])接收时间辍(1970 纪元年后经过的浮点秒数)并返回当地时间下的时间元组 t(t.tm_isdst 可取 0 或 1,取决于当地当时是不是夏令时)
  • time.localtime返回struct_time的时间格式
  • 以时间元祖(struct_time)的形式返回
  • 0~5个元素就是从年到秒
  • 表现你的类:strrepr
  • 重写他们,如示例1
  • 重写 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乱入,生成器

  • 无需类和方法对象,仅需要普通函数即可实现,相比迭代器,生成器使得ython更为简洁
  • 生成器需要在普通函数里有 yield 语句
  • 生成器使得协同程序可以实现
  • 可参考: 提高你的 Python:解释 yield 和 Generators(生成器)—— 鱼C工作室
  • 协同程序(生成器的应用)

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

    yield相当于普通函数中的return语句,它在返回数据的同时,能将函数执行过程暂停于此
    用生成器实现的几个例子,示例1~2

    
    # 用生成器实现的几个例子,示例1~
    # 生成器运行过程示例,示例1
    def myGen():
        print("生成器被执行了!")
        yield 1
        yield 2
        yield 3
    
    myG = myGen()
    next(myG)
    next(myG)
    for i in myGen():
        print(i)
    
    print('-------------------------------')
    
    # 斐波那契也可以用生成器来实现,示例2
    def fibs():
        a = 0
        b = 1
        while True:         # 生成器随时都会暂停,不用担心死循环
            a, b = b, a + b
            yield a
    
    for each in fibs():
        if each > 100:
            break
        print(each, end=' ')
    

    四种推导式(包含了生成器推导式)

    # 列表推导式
    a = [i for i in range(100) if not (i % 2) and (i % 3 )]     # 100以内,能被2整除,但不能被3整除的所有整数
    print(a)
    print('-------------------------------')
    
    # 字典推导式:
    a = {i:i % 2 == 0 for i in range(10)}       # 10以内是否为偶数
    print(a)
    print('-------------------------------')
    
    # 集合推导式:
    a = {i for i in [1, 2, 3, 3, 3, 6, 5, 5, 6, 7, 7, 8]}
    print(a)
    print('-------------------------------')
    
    # (元组)生成器推导式:
    e = (i for i in range(5))
    print(next(e))
    print(next(e))
    print(next(e))
    for each in e:
        print(each, end=' ')
    print('\n-------------------------------')
    
    # 生成器推导式可作为函数的参数:把sum里面的内容是生成器推导式,把生成器推导式里生成的数据都加起来,计算100以内不能被2整除的数的和
    print(sum(i for i in range(100) if i % 2))
    

    🔰模块 (OLD:P51~P53)

    P53 模块就是程序

    介绍

    容器是对数据的封装
    函数是对语句的封装
    类是对方法和属性的封装
    模块就是对程序的封装,一个python文件

    命名空间

    模块.方法,如: hello.hi()

    导入与调用模块

  • 方法一、import 模块名
  • 调用:模块.模块中的函数名(参数)
  • 方法二、from 模块名 import 函数名,函数名可以使用*代替,用于导入模块中的所有命名空间
  • 调用:模块中的函数名(参数)
  • 缺点:可能出现模块中的函数名与程序中的函数名重复的情况
  • 方法三、import 模块名 as 新模块名 推荐!
  • 调用:新模块名.模块中的函数名(参数)
  • 优势:重命名模块名,可使长的模块名缩短
  • 示例 – 模块

    # TemperatureConversion.py
    def c2f(cal):
        fah = cal * 1.8 + 32
        return fah
    def f2c(fah):
        cal = (fah - 32)/1.8
        return cal
    

    示例 – 调用

    # 方法一
    import TemperatureConversion 
    
    print("32 摄氏度 = %.2f 华氏度" % TemperatureConversion.c2f(32))
    print("99 华氏度 = %.2f 摄氏度" % TemperatureConversion.f2c(99))
    
    # 方法二
    from TemperatureConversion import c2f, f2c  # 或from TemperatureConversion import * 
    
    print("32 摄氏度 = %.2f 华氏度" % c2f(32))
    print("99 华氏度 = %.2f 摄氏度" % f2c(99))
    
    # 方法三
    import TemperatureConversion as tc  
    
    print("32 摄氏度 = %.2f 华氏度" % tc.c2f(32))
    print("99 华氏度 = %.2f 摄氏度" % tc.f2c(99))
    

    P52 name == ‘main’、搜索路径和包

    模块的好处:

  • 实现代码的重复利用:先封装成模块,需要使用时,导入模块,再调用函数即可
  • if name == 'main’

    注意:

  • 模块文件在前期编写、调试的时候,要添加测试程序,已测试模块中的各个方法能否正常运行,如示例1
  • # 示例1
    def c2f(cal   ):
        return cal * 1.8 + 32
    
    def f2c(fah):
        return (fah - 32)/1.8
    
    def test():
        print("测试:", "0摄氏度 = %.2f 华氏度\n" % c2f(0))
        print("测试:", "0华氏度 = %.2f 摄氏度" % f2c(0))
    
    test()
    

    __name__变量如何使用?

  • 如果在主程序中运行__name__,会返回__main__
  • 如果在主程序中使用模块调用__name__ ,如tc.__name__,会返回模块名
  • if name = _ ‘main’_:可以方便避免在主程序调用模块的时候,运行到模块中的test()程序

  • if ``__name__ == _ ``_'main'_:_ test()加入主程序,这就是个程序运行入口
  • if ``__name__ == _ ``_'main'_:_ test()加入模块文件,那么它里面的test()就不会运行,因为不满足if的条件
  • 搜索路径

  • 查看搜索路径:import sys sys.path
  • 最佳存放模块的路径:site-packages 文件夹
  • 添加搜索路径
  • sys.path.append('你的模块目录路径'),如sys.path.append("C:\\Python39\\test")
  • 包(package)

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

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

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

    P53 像一个极客去思考

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

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

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

    快速掌握模块的用法

    1. 先导入模块,然后调用__doc__属性,查看模块简介。如import timeit print(timeit.__doc__)
    2. 使用dir()可以查询到该模块定义了哪些变量、函数和类。如 dir(timeit)
    3. 其中的__all__属性:timeit.__all__可以给我们提供可供外界调用的东西
    4. 其中的__file__属性:timeit.__file__可以给我们提供模块源代码所在位置
    5. 阅读源代码可以快速提升能力哦!
    6. 使用help(timeit) 帮助文档,比print(timeit.__doc__)调出来的简单一点
  • 注意:
  • 不是所有模块都有__all__属性
  • 如果模块有__all__属性,那么使用*导入全部命名空间的话 如from timeit import *,只有timeit.__all__显示出来的才能被导入
  • 因此,推荐在编写模块文件时,将所有对外提供的接口和类,都设置到__all__属性的列表中
  • IT英语:
  • IT英语版块 ——FishC
  • 每天进步一点点,做最好的自己[专辑] ——FishC:破渔网兜兜
  • 最后 timeit模块太有用了,建议阅读源码等
  • [扩展阅读] timeit 模块详解(准确测量小段代码的执行时间)
  • 🔰爬虫

    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')
    
  • 编译标志

  • 通过编译标志,可以修改正则表达式的工作方式
  • 可同时使用多个编译标志,中间用 | ,如re.I | re.M
  • [扩展阅读] Python3 如何优雅地使用正则表达式(详解三)
  • 开启详细正则表达式模式

  • 此模式支持空格和tab缩进和注释,能让表达式看上去更直观、易读
  • re.VERBOSE
  • P61 论8:正则表达式4

  • srearch()方法
  • 有两种调用方法:
  • 模块形式调用:re.search(pattern, string, flag=0)
  • 参数
  • pattern参数:模式
  • string参数:要搜索的字符串
  • flag参数:编译标志位
  • 返回结果的捕获
  • .group(),如下示例1
  • result = re.search(f" (\w+) (\w+)", ‘it is fishc.com’)
  • print(result.group())
  • .group()可以设置参数
  • .group(1),参数代表子组索引。没有参数默认提取所有内容
  • 返回匹配的起始位置及范围
  • .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
  • HTTP 状态码大全
  • 400-499的异常来自客户端,500-599异常来自服务器
  • 示例2
  • 两种常用的写法
  • 示例3
  • # URLError 处理。示例1
    import urllib.request
    import urllib.error
    
    req = urllib.request.Request("http://wwww.ooxx123.com")   # 随便一个不存在的网址
    try:
        urllib.request.urlopen(req)
    except urllib.error.URLError as e:
        print(e.reason)
    
    # HTTPError处理,如状态码404.示例2
    import urllib.request
    import urllib.error
    
    req = urllib.request.Request("http://wwww.baidu.com/ooxx.html")   # 随便一个不存在的网址
    try:
        urllib.request.urlopen(req)
    except urllib.error.HTTPError as e:
        print(e.code)
        print(e.read())     # 读取错误页面,可对这个页面可以进行进一步处理
    
    # 处理异常的两种写法,示例3
    from urllib.request import Request, urlopen
    from urllib.error import URLError, HTTPError
    req = Request('http://baidu.com')  # Request(someurl)
    try:
        response = urlopen(req)
    
    except HTTPError as e:
        print('The server couldn\'t fulfill the request.')
        print('Error code:', e.code)
    except URLError as e:
        print('We failed to reach a server')
        print('Reason: ', e.reason)
    else:
        pass
    # everything is fine
    
    
    # 第二种
    from urllib.request import Request, urlopen
    from urllib.error import URLError
    req = Request('http://baidu.com')  # Request(someurl)
    try:
        response = urlopen(req)
    except URLError as e:
        if hasattr(e, 'reason'):
            print('We failed to reach a server')
            print('Reason: ', e.reason)
        elif hasattr(e, 'code'):
            print('The server couldn\'t fulfill the request.')
            print('Error code:', e.code)
    else:
        pass
    # everything is fine
    

    P63 论10:安装 scrapy

    Scrapy介绍:

    Scrapy是用纯Python实现的一个爬取网站数据、提取结构性数据的应用框架。有了框架,用户只需要定制开发几个模块就可以轻松的实现一个爬虫,用来抓取网页内容以及各种图片。
    最初是为了页面抓取所设计的,也可以应用在获取API所返回的数据或者通用的 网络爬虫。

  • 优点:快速、高层次的屏幕抓取和web抓取框架
  • 用途:数据挖掘、监测和自动化测试。
  • python如果安装多个版本并不冲突,他们独立存在

  • 安装Scrapy所需要的前提和步骤
    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()
    
  • 小甲鱼在https://fishc.com发布的,供参考(付费内容…):
  • Tkinter 窗口组件:Label
  • Tkinter 窗口组件:Menu
  • Tkinter 窗口组件:Text
  • Tkinter 窗口组件:Listbox
  • Tkinter 窗口组件:Button
  • Tkinter 窗口组件:Entry
  • Tkinter 窗口组件:Canvas
  • Tkinter 窗口组件:Spinbox
  • Tkinter 窗口组件:Toplevel
  • Tkinter 窗口组件:LabelFrame
  • Tkinter 窗口组件:PanedWindow
  • Tkinter 窗口组件:Checkbutton
  • Tkinter 窗口组件:Frame
  • Tkinter 窗口组件:Scrollbar
  • Tkinter 窗口组件:Radiobutton
  • Tkinter 窗口组件:Message
  • Tkinter 窗口组件:Scale
  • Tkinter 窗口组件:Menubutton
  • Tkinter 窗口组件:OptionMenu
  • P66 GUI的终极选择:Tkinter 3

    示例1 – Checkbutton 组件,实现勾选 ,

    # Checkbutton 组件,实现勾选 ,示例1
    from tkinter import *
    
    root = Tk()
    
    v = IntVar()    # Tkinter变量,用于表示该按钮是否被选中
    c = Checkbutton(root, text="测试一下", variable=v)
    
    c.pack()
    
    lable = Label(root, textvariable=v)     # v用来存放 Checkbutton 的选中状态
    lable.pack()
    
    mainloop()
    

    示例2 – Checkbutton 多选,

    # Checkbutton 多选,示例2
    from tkinter import *
    
    root = Tk()
    
    GIRLS = ["西施", "貂蝉", "王昭君", "杨玉环"]
    v = []              # 定义个空列表,存放勾选状态
    for girl in GIRLS:
        v.append(IntVar())              # 如果被选中,IntVar() 被赋值为1,否则为0
        b = Checkbutton(root, text=girl, variable=v[-1])
        b.pack(anchor=W)                # anchor 用于指定显示位置,W=western=左对齐
    
    mainloop()
    

    示例3 – Radiobutton 单选按钮。

    # Radiobutton 单选按钮。示例3
    from tkinter import *
    
    root = Tk()
    
    v = IntVar()
    Radiobutton(root, text="One", variable=v, value=1).pack(anchor=W)   # 通过对比 variable 和value 的值,实现勾选的显示
    Radiobutton(root, text="Two", variable=v, value=2).pack(anchor=W)
    Radiobutton(root, text="Three", variable=v, value=3).pack(anchor=W)
    
    mainloop()
    

    示例4 – Radiobutton 单选按钮,用循环实现。

    # Radiobutton 单选按钮,用循环实现。示例4
    from tkinter import *
    
    root = Tk()
    
    LANGS = [
        ("Python", 1),
        ("Perl", 2),
        ("Ruby", 3),
        ("Lua", 4)]
    
    v = IntVar()
    v.set(1)            # 设置默认值1
    for lang, num in LANGS:
        # b = Radiobutton(root, text=lang, variable=v, value=num)
        # b.pack(anchor=W)
        b = Radiobutton(root, text=lang, variable=v, value=num, indicatoron=False)  # indicatoron=False 将显示为文字按钮效果
        b.pack(fill=X)      # X = 横向填充
    mainloop()
    

    示例5 – 用 LabelFrame 实现组件分组。

    from tkinter import *
    
    root = Tk()
    
    group = LabelFrame(root, text="最好的脚本语言是?", padx=5, pady=5)
    group.pack(padx=10, pady=10)
    
    LANGS = [
        ("Python", 1),
        ("Perl", 2),
        ("Ruby", 3),
        ("Lua", 4)]
    
    v = IntVar()
    v.set(1)
    for lang, num in LANGS:
        b = Radiobutton(group, text=lang, variable=v, value=num)
        b.pack(anchor=W)
    
    mainloop()
    

    P67 GUI的终极选择:Tkinter 4

    输入框组件

    示例1 – 输入框 Entry 组件,增加、删除。

  • 增加、删除,用insert、delete。如示例1
  • 获取输入框内容:
  • 用 组件的get()
  • 用 变量value的 get()
  • grid 以表格形式管理组件。如示例2
  • # 输入框 Entry 组件,增加、删除。示例1
    from tkinter import *
    
    root = Tk()
    e = Entry(root)
    e.pack(padx=20, pady=20)
    
    e.delete(0, END)          # 清空输入框
    e.insert(0, "默认文本...")  # 设置输入内容
    
    mainloop()
    

    示例2 – 获取输入框内容。

    # 获取输入框内容。示例2
    from tkinter import *
    
    root = Tk()  # 创建主窗口
    
    Label(root, text="作品:", padx=20, pady=10).grid(row=0, column=0)  # grid(row=0, column=0)表示第0行第0列
    Label(root, text="小甲鱼:").grid(row=1, column=0)  # 第1行第0列
    
    e1 = Entry(root)  # 输入框
    e2 = Entry(root)  # 输入框
    e1.grid(row=0, column=1, padx=10, pady=5)
    e2.grid(row=1, column=1, padx=10, pady=5)
    
    def button1_show():
        print("作品:《%s》" % e1.get())
        print("作者:%s" % e2.get())
    
    Button(root, text="获取信息", command=button1_show)\
        .grid(row=2, column=0, sticky=W, padx=10, pady=10)  # sticky=W 表示位置在西(左)
    Button(root, text="退出", command=root.quit)\
        .grid(row=2, column=1, sticky=E, padx=10)
    
    
    mainloop()
    

    示例3 – 账号密码输入,获取。

    # 账号密码输入,获取。示例3
    from tkinter import *
    
    root = Tk()
    
    Label(root, text="账号:").grid(row=0, column=0)
    Label(root, text="密码:").grid(row=1, column=0)
    
    v1 = StringVar()
    v2 = StringVar()
    
    e1 = Entry(root, textvariable=v1)
    e2 = Entry(root, textvariable=v2, show="*")  # * 代替填充
    e1.grid(row=0, column=1, padx=10, pady=5)
    e2.grid(row=1, column=1, padx=10, pady=5)
    
    def show():
        print("账号:%s" % e1.get())
        print("密码:%s" % e2.get())
    
    Button(root, text="芝麻开门", command=show)\
        .grid(row=2, column=0, sticky=W, padx=10, pady=5)
    Button(root, text="退出", command=root.quit)\
        .grid(row=2, column=1, sticky=E, padx=10)
    
    mainloop()
    

    示例4 – 输入验证。

    启用验证开关:validate选项
    验证函数:validatecommand,返回True或False。通过Entry组件的 get() 方法获得该验证字符串

    # 输入验证。示例4
    # 在第一个输入框输入,并通过tab键将焦点转移到第二个输入框时,验证功能被触发
    from tkinter import *
    
    master = Tk()
    
    def test():
        if e1.get() == "小甲鱼":
            print("正确!")
            return True
        else:
            print("错误!")
            e1.delete(0, END)   # 清空输入框
            return False
    
    v = StringVar()
    
    e1 = Entry(master, textvariable=v, validate="focusout", validatecommand=test) # validate="focusout"表示焦点移出时,validatecommand=test自动触发验证函数
    e2 = Entry(master)
    e1.pack(padx=10, pady=10)
    e2.pack(padx=10, pady=10)
    
    mainloop()
    

    示例5 – invalidcommand函数。

    invalidcommand函数只有在validatecommand的返回值 为False时才被调用

    # invalidcommand函数。示例5
    from tkinter import *
    
    master = Tk()
    v = StringVar()
    
    def test():
        if e1.get() == "小甲鱼":
            print("正确!")
            return True
        else:
            print("错误!")
            e1.delete(0, END)
            return False
    
    def test2():
        print("我被调用了...")
    
    e1 = Entry(master, textvariable=v, validate="focusout",\
               validatecommand=test, invalidcommand=test2)
    e2 = Entry(master)
    e1.pack(padx=10, pady=10)
    e2.pack(padx=10, pady=10)
    
    mainloop()
    

    示例6 – validatecommand的其他参数。

  • validatecommand的参数
  • %P 当输入框的值允许改变的时候,该值有效;该值为输入框的最新文本内容
  • %v 该组件当前的 validata 选项的值
  • %W 该组件的名称
  • # validatecommand的其他参数。示例6
    from tkinter import *
    
    master = Tk()
    v = StringVar()
    
    def test(content, reason, name):
        if content == "小甲鱼":
            print("正确!")
            print(content, reason, name)
            return True
        else:
            print("错误!")
            print(content, reason, name)
            return False
    
    testCMD = master.register(test)
    e1 = Entry(master, textvariable=v, validate="focusout",\
               validatecommand=(testCMD, '%P', '%v', '%W'))
    e2 = Entry(master)
    e1.pack(padx=10, pady=10)
    e2.pack(padx=10, pady=10)
    
    mainloop()
    

    示例7 – 计算器。

    validate选项指定为“key"的时候,有任何输入操作都会被拦截去验证,如果返回True,才会给关联的变量textvariable赋值

  • state:
  • Entry可以设置的状态:NORMAL,DISABLED(禁用选中、拷贝、修改)或"readonly"(支持选中和拷贝,不能修改)
  • 默认值时NORMAL
  • 如果值为DISABLED或"readonly",那么调用 insert() 和 delete() 都会被忽略
  • # 计算器。示例7
    from tkinter import *
    
    root = Tk()
    frame = Frame(root)
    frame.pack(padx=10, pady=10)
    
    v1 = StringVar()    # 第一个数
    v2 = StringVar()    # 第二个数
    v3 = StringVar()    # 计算结果
    
    def test(content):
        return content.isdigit()
    
    testCMD = frame.register(test)
    e1 = Entry(frame, width=10, textvariable=v1, validate="key",validatecommand=(testCMD, '%P'))\
               .grid(row=0, column=0)
    
    Label(frame, text="+").grid(row=0, column=1)        # 显示 +
    
    e2 = Entry(frame, width=10, textvariable=v2, validate="key",validatecommand=(testCMD, '%P'))\
               .grid(row=0, column=2)
    
    Label(frame, text="=").grid(row=0, column=3)        # 显示 =
    
    e3 = Entry(frame, width=10, textvariable=v3, state="readonly")\
               .grid(row=0, column=4)   # state="readonly" = 只读模式,v3的数据赋值给textvariable进行输出显示
    
    
    def calc():
        result = int(v1.get()) + int(v2.get())
        v3.set(str(result))
    
    Button(frame, text="计算结果", command=calc).grid(row=1, column=2, pady=5)
    
    mainloop()
    

    P68 GUI的终极选择:Tkinter 5

    Listbox组件
    列表框+滚动条 = 支持大量选项的情况下

    示例1 – Listbox简单示例,添加、删除。

  • 添加 .insert()
  • 删除 .delete()
  • selectmode选项的四种选择模式
  • SINGLE 单选
  • BROWSE 单选,通过鼠标拖动或方向键
  • MULTIPLE 多选
  • EXTENDED 多选,但需同时按住Shift或Ctrl
  • # Listbox简单示例,添加、删除。示例1
    from tkinter import *
    
    master = Tk()#创建主窗口
    
    theLB = Listbox(master, setgrid=True, selectmode=SINGLE)
    theLB.pack()
    
    # 添加,基础方法
    # theLB.insert(0, '你是猪')          # 0 表示第一个位置
    # theLB.insert(END, '小甲鱼不是猪')   # END 表示最后一个位置
    
    # 添加,循环添加
    for item in ["鸡蛋", "鸭蛋", "鹅蛋", "李狗蛋"]:
        theLB.insert(END, item)     # END表示列表中每一项的最后位置
    
    # 删除,基础方法
    # theLB.delete(0, END)        # 删除所有项目,两个参数分别是起始位置、结束位置
    # theLB.delete(2)             # 删除指定
    
    # 删除,通过按钮删除
    theButton = Button(master, text="删除它", command=lambda x=theLB:x.delete(ACTIVE))   # ACTIVE表示当前选中的数据
    theButton.pack()                                # ↑ lambda表达式
    
    mainloop()
    

    示例2 – height 选项控制行数。

    # height 选项控制行数。示例2
    from tkinter import *
    
    root = Tk()
    
    theLB = Listbox(root, setgrid=True, selectmode=BROWSE, height=11)   # height=11 显示11行
    theLB.pack()
    
    for item in range(11):
        theLB.insert(END, item)
    
    theButton = Button(root, text="删除", command=lambda x=theLB: x.delete(ACTIVE))
    theButton.pack()
    
    mainloop()
    

    示例3 – 滚动条 Scrollbar组件。

  • 在组件上安装垂直滚动条:
    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官网:
  • https://www.pygame.org/
  • 安装
  • 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)
  • 帮助文档
  • https://www.pygame.org/docs
  • 示例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()
    

    示例 – 方向键控制小乌龟

    方向键控制小乌龟

  • 按键对照表
  • https://www.pygame.org/docs/ref/key.html
  • 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

    来源:COOL_DREAM_

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【python教程】– 入门 | 小甲鱼《零基础入门学Python》教程笔记(知识点详细、源码可复制)全

    发表评论