Python基础教程(第三版)

Python基础教程(第三版)

写在前面:
本文是根据[挪] Magnus Lie Hetland的《Python基础教程(第三版)》总结的部分个人笔记
电子版资料想要的可以私信滴滴我哦
如有错误欢迎交流指出!

文章目录

  • Python基础教程(第三版)
      • 第一章:基础知识
      • 第二章:列表和元组
        • Part 1:列表
        • Part 2:元组
      • 第三章:字符串和字典
        • Part 1:设置字符串格式
        • Part 2:字符串方法
        • Part 3:字典及其方法
      • 第四章:条件、循环及其他语句
        • Part 1:再谈print和import
        • Part 2:赋值语句
        • Part 3:条件语句与布尔值
        • Part 4:循环与迭代
        • Part 5:del、exec与eval
      • 第五章:函数
        • Part 1:自定义函数
        • Part 2:函数的参数
        • Part 3:作用域
      • 第六章:抽象
      • 第七章:异常
      • 第八章:特殊方法、特性和迭代器
        • Part 1:构造函数
        • Part 2:元素访问与特殊方法
        • Part 3:特性 property
        • Part 4:迭代器和生成器

第一章:基础知识

  • 1、整除运算\\与取余运算%规则: 整除运算结果向负无穷取整(支持负数整除,整除后离0更远);取余运算与整除运算相互对应。

  • 2、python变量那些事: python的变量必须赋给初始值,即python变量没有默认值;变量名不能以数字开头。

  • 3、二进制八进制十六进制:0xAF十六进制175,0o10八进制8,0b10二进制2。

  • 4、round()的取数规则: 将浮点数取整为与之更接近的数,当距离两个整数一样近时取整到偶数(round(2.5)结果为2)。

  • 6、自动拼接字符串:print('hello''world''!')输出结果为helloworld!

  • 7、字符串表示strrepr

    print(str("hello\nworld!"))  # str以合理的方式将值转换为用户可以理解的字符串
    print(repr("hello\nworld!")) # repr会返回对象的规范字符串表示形式
    print(repr(1234),repr([1234])) 
    # print函数输出对象的内容而非对象本身,例如打印字符串不打印引号
    # 即:>>> repr("hello\nworld!")输出为'"hello\nworld!"'
    # hello
    # world!
    # 'hello\nworld!'
    # 1234 [1234]
    
  • 8、长字符串和原始字符串:

    (1)""" str """三引号表示的长字符串可包含单引号和双引号,不需要反斜杠转义(常规字符可用反斜杠横跨多行);

    (2)原始字符串是前缀为r的字符串,将屏蔽对字符串中反斜杠的特殊处理,将每个字符都保持原样。**一个例外是,引号需要像通常一样进行转义,这样意味着用于转义的反斜杠也将包含到最后的字符串中。**如:r'Let\'s go'

    原始字符串和repr(object)不同,前者是屏蔽特殊处理的字符串,后者则是返回一个对象的字符串格式;相同的地方是不能以\结尾,因为引号会被转义。

  • 9、Unicode、bytes和bytearray:

    1、Unicode:每个Unicode字符都用一个码点(codepoint)来表示,码点是Unicode标准给每个字符指定的数字;有一种指定的Unicode字符通用机制:使用16位或32位的十六进制字面量(分别加上前缀\u\U)或者使用字符的Unicode名称(\N{name})

    UTF-8、UTF-16、UTF-32都是将码点转换为程序数据的转换格式

    2、bytes与bytearray对象:将字符串通过str.encode()函数按照一定的编码规则(默认为UTF-8)生成一个以b为前缀的不可变字节序列,该不可变字节序列即为bytes对象;bytearray则是可变字节序列,想要对其中的字符进行替换时必须指定为0~255的值

    str1 = "hellox\a"
    print(str1.encode)  # 该种情况不会报错,注意区别
    print(str1.encode())
    # 
    # b'hellox\x07' 
    # bytes对象中的每一个字符类型均为int:type(str1.encode()[0]) == int
    # 对应的int值即为一定编码规则下的对应值
    
    x = bytearray(b'hello!')
    x[1] = ord('u') # 等同于ord(b'u')
    print(x)
    # ord()函数接收一个字符作为参数,返回对应的Unicode码点
    # 与chr()函数(对于八位ascii字符)或unichr()函数(对于Unicode对象)相对应
    # python3不再支持unichr()改为chr()
    

    python中数据转为二进制后非以二进制形式存储,而是以bytes类型存储:
    字符串中的字符若在ascii码范围内时,在bytes中不变;在范围之外时储存形式为字符的码点在对应规则转化下的编码格式。例:

    Unicode/UCS-4(十六进制) 字节数 UTF-8编码格式(二进制)
    000000-00007F 1 0xxxxxxx
    000080-0007FF 2 110xxxxx 10xxxxxx
    000800-00FFFF 3 1110xxxx 10xxxxxx 10xxxxxx
    010000-10FFFF 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
    # Π在Unicode标准中的码点为\u03c0(在第二层,两个字节表示)
    # 对于ascii编码,当不在range(128)时会用前128个字符对Π进行编码,编码结果为Unicde码点
    # 根据上方的对应方式翻译\u030为二进制码:1100 1111 1000 0000
    >>'Π'.encode()
    b'\xcf\x80' # 该二位十六进制码对应的二进制为 1100 1111 1000 0000
    
  • 补充:

int(x,base = 10)进行字符串强制转换时,会将x表示的字符串以base进制转化为10进制输出

AZ对应ASCII码为6590,az为97122

附:x为小数时不能有base参数,否则将报错TypeError: int() can't convert non-string with explicit base

函数 描述
math.ceil(number) 以浮点数的方式返回向上取整的结果
math.floor(number) 以浮点数的方式返回向下取整的结果
math.sqrt(number) 返回平方根;不能用于负数
cmath.sqrt(number) 返回平方根;能用于负数

cmath是用于处理复数的模块,不同库的同名函数注意调用时区分


第二章:列表和元组

Part 1:列表
  • **1、关于序列的几种操作:**索引、迭代、切片、相加、相乘和成员资格审查

    对序列进行迭代意味着对其中的每一个元素都执行特定的操作

  • **2、切片注意:**步长不能为0,步长为负数时表示从右向左提取元素(起点总是由步长的正负决定);切片时注意起点与终点的索引大小关系

  • 3、初始化指定长度的列表:ls = [None]*n定义一个长为n的空列表

  • 4、列表操作(部分):

    (1)删除指定元素:del list1[n]删除list1中索引为n的元素;

    (2)给切片赋值:(该操作可以改变切片的长度)

    name = list('Perl')
    name[1:] = 'ython'  # 输出:['P', 'y', 't', 'h', 'o', 'n']
    # 标准做法为将赋值字符串放入[]内,不放入时若为切片则发生隐式强制转换
    # 即若name = 'ython' 则type(name) == str;上例中仍为list
    

    切片操作也能插入新的元素:

    name = list('Perl')
    name[1:1] = 'ython'  # 输出:['P', 'y', 't', 'h', 'o', 'n', 'e', 'r', 'l']
    
  • 5、列表方法(部分):

    (1)list.clear():方法clear就地清空列表的内容,返回值为None;

    (2)list.copy():方法copy复制列表,常规赋值只是将另一个名称关联到列表;

    list.copy()等效于list()list[:]

    (3)list.count(value):方法count计算指定元素在列表中出现的次数并返回;

    (4)append(object)extend(object)extend()可以将多个元素放入列表,要求放入的元素作为一个序列传入函数,序列中的每个元素会依次加入列表中

    ls = [1]
    ls.append('2');ls.append((3,));ls.append({4,5}) # 字符串、元组、集合
    print(ls)
    # 输出:[1, '2', (3,), {4, 5}]
    # append方法只能加入一个任意对象,该对象作为整体加在原列表最后
    
    ls.extend('[123],123')
    ls.extend([1,2,3])
    ls.extend((1,2,'2'))
    ls.extend({20,2,3})
    print(ls)
    # [1, '2', (3,), {4, 5}, '[', '1', '2', '3', ']', ',', '1', '2', '3', 1, 2, 3, 1, 2, '2', 2, 3, 20]
    

    (5)list.index(value):方法index在列表中查找指定值第一次出现的索引,找不到时会报异常

    (6)list.insert(index, object):方法insert用于将一个对象插入列表的指定位置

    ls = [ 1, 2, 3, 4, 5];print(ls.insert( 1, 123), ls)
    # None [1, 123, 2, 3, 4, 5]
    

    (7)list.pop(index = -1):方法pop从列表中删除一个元素(默认为最后一个),并且返回这个元素

    pop是唯一既修改列表又返回非None值的列表方法,可以与append和insert相互结合完成对栈和队列的操作

    (8)list.remove(value):方法remove用于删除第一个为指定值value的元素,删除非列表内的值会报错

    (9)list.reserve():方法reverse按照相反的顺序排列列表中的元素

    reverse只能用于列表,reversed(seq)函数返回一个反转迭代器,可以对序列进行转换

    (10)list.sort()sorted(list):均用于列表的排序,前者类内方法为就地排序,直接对原列表修改;后者是一个返回排序后列表的函数,原列表不变(可以用于任何序列,但总返回一个列表)

    sort方法和sorted函数均有两个参数key和reverse:key可以为函数,赋给列表每个元素以权值,然后列表按照权值大小排序;reverse默认为False,从大到小排序,True时从小到大排序

    注意两者的调用区别】:sorted不是列表方法

Part 2:元组
  • **1、元组:**元组也是序列,与列表的唯一差别时元组不能被修改,单元素元组末尾必须加逗号

  • **2、元组的作用:**元组作为映射中的键(以及集合的成员),而列表不行;Python中部分内置函数和方法返回元组

    集合内元素不可变,因此列表不能为集合的成员

  • 补充:

    函数 描述
    len(seq) 返回序列的长度
    max(args) 返回序列或一组参数中的最大值(可以为多个非序列的参数)
    min(args) 返回序列或一组参数中的最小值(同上)

第三章:字符串和字典

Part 1:设置字符串格式
  • format方法:将变量值填入字符串内的花括号内部

    >>> "{foo} {1} {bar} {0}".format(1,2,bar = 3, foo = 4)# 位置参数
    '4 2 3 1'
    >>> "{}"
    
  • 设置字符串格式的基本思想就是对字符串调用方法format,并提供要设置其格式的值。每个值都要被插入字符串中,以替换用花括号括起来的替换字段,若要保留花括号则使用两个花括号来指定

    >>> "{{ceci n'est pas une replacement field }}".format()
    "{ceci n'est pas une replacement field }"  # 没有要插入的值,所以保持原状
    

    替换字段由以下部分组成:

    • 字段名:索引或者标识符,指出要设置哪个值的格式并使用结果来替换该字段。

    • 转换标志:跟在感叹号后面的单个字符,当前支持的字符包括r(repr)、a(ascii)、s(str)。如果指定了转换标志,将不再使用对象本身的格式设置机制,而是使用指定的函数将对象转换为字符串,再做进一步的格式设置

    • 格式说明符:跟在冒号后面的表达式,这种表达式使用微型格式指定语言,指定详细的最终输出格式。

  • 基本转换:

    >>> print("{pi!s} {pi!r} {pi!a}".format(pi = 'Π'))
    Π 'Π' '\u03c0'
    

    ​ 以上三个转换标志指定分别使用str、repr、ascii进行转换:str即str()进行强转,repr返回指定对象的字符串表示形式,其中ascii函数创建只包含ASCII字符的表示。

    ​ 同时可以使用格式说明符来限制输出的格式(即冒号后面)'{num:.2f}'.format(num = 1.2345)将浮点类型限制到两位。

    字符串格式设置中的类型说明符
类型 含义
b 将整数表示为二进制数
c 将整数解读为Unicode码点
d 将整数视为十进制数进行处理,是整数默认的说明符
e 使用科学表示法来表示小数,用e来表示指数
E 与e相同,但是用E来表示指数
f 将小数表示为定点数(默认为六位小数)
F 与f相同,但是对于特殊值(nan和inf无穷大)使用大写表示(NAN和INF)
g 自动在定点表示法和科学表示法之间做出选择。默认用于小数的说明符,但在默认情况下至少有1位小数
G 与g相同,但是用大写来表示指数和特殊值
n 与g相同,但是插入随区域而异的数字分隔符
o 将整数表示为八进制数
s 保持字符串的格式不变
x 将整数表示为十六进制数并使用小写字母(默认不带前缀)
X 与x相同但使用大写字母
% 将数字表示为百分比值(乘以100,按说明符f设置格式,再在后面加上%)
  • 格式化标识符:{num:<填充字符><对齐><宽度><千位分隔符>.<精度><类型说明符>}.format(num = )其中千位分隔符为,(不能与类型说明符n同时出现)

    1、指定填充字符时必须指定对齐方式,'{num:$10.2f}'.format(num = 3.1415)报错# ValueError: Invalid format specifier

    2、指定宽度后,字符串默认为左对齐,数的默认对齐方式是右对齐;变量为字符串时没有后三个标识符(千分位、精度和类型说明符)

    print('{num:20,.3f}'.format(num = 987654321)) # 设置宽度为20、保留三位小数且使用千位分隔符
    #     987,654,321.000
    
    print('{num:$<10.2f}\n{num:$^10.2f}\n{num:$>10.2f}'.format(num = 3.1415)) 
    # 填充为'$'宽度为10(不指定填充时默认为空格),保留两位小数,从左到右依次为左对齐、居中、和右对齐
    #3.14$$$$$$   
    #$$$3.14$$$
    #$$$$$$3.14
    
    # 其他符号:‘=’、‘+’、‘#’(最后一种略)
    from math import pi
    print('{:+10.2f}\n{:=10.2f}'.format(pi,-pi))
    # 符号说明符号+,放在对齐说明符之后
    # =指定将填充字符放在符号和数字之间,与对齐符同时出现没有效果
    #     +3.14
    #-     3.14
    
Part 2:字符串方法
  • 1、str.center(width, char = ' '):方法center通过在两边添加填充字符(默认为空格来让字符串居中)

  • 2、str.find(str1, start, end):方法find在字符串中查找子串,如果找到就返回子串的第一个字符的索引,否则返回-1(可以指定查找的起点和终点,包含起点不包含终点)

  • 3、str.join(seq):join方法将分隔符str插入元素均为字符串的序列seq中,与split相反,用于合并序列的元素

    split(sep,num)函数接收两个参数,第一个参数是分隔符,第二个参数表示分割次数,在分隔符处分开并返回一个列表(分隔符会被删除,不在列表中)

  • 4、str.lower():方法lower返回字符串的小写版本,可以用于屏蔽大小写区分

  • 5、str.replace(a,b):用字符串b替换字符串a并返回替换后的字符串(replace a with b),原来的字符串不变

  • 6、str.strip():方法strip将字符串开头和末尾的空白删除,并返回删除后的结果,原来的字符串不变(可以在括号内指定删除哪些字符)

  • 7、str.translate():方法translate与replace一样替换字符串的特定部分,但是不同的是它只能进行单字符替换,优势在于能够同时替换多个字符,效率比replace更高

    # 使用translate()之前必须先创建一个转换表,转换表指出不同Unicode码点之间的转换关系
    # 对str类型调用maketrans()方法生成转换表,该方法接收两个长度相等的字符串str1和str2,指定str1中的每一个字符都替换为str2中的相应字符,第三个参数可以指定删除哪些字母
    table = str.maketrans('cs','kz','w')# cs替换为kz并删除w
    print(table)
    # {115:112,99:107,119:None} 码点的映射(创建了对应的映射,删除后对应为None)
    
    print('i want to play cs'.translate(table))
    # i ant to play kz
    
Part 3:字典及其方法
  • 将字符串格式设置应用于字典(format_map):

    phoneBook = {'lhz':1234,'zhl':4321}
    phoneBook1 = {'lhz':1234,123 :4321}
    phoneBook2 = {'lhz':1234,(1,2,3):4321}
    print('lhz\'s phone number is {lhz}'.format_map(phoneBook))
    # 仅当键为str时,在大括号内放入键的内容(上述phoneBook1和2均无法访问第二对键值)
    # lhz's phone number is 1234
    

    dict.keys()来获取字典的键,返回值为class_keys类的对象,dict.values()同理

  • 1、dict.clear():方法clear可以清除所有的字典项,返回值为None

  • 2、dict.copy():浅层复制,替换不影响,修改会影响(修改对应值为列表的情况)

    deepcopy():将dict作为参数传入进行深层复制(注意与copy的调用方式不同),开辟新的空间并复制值,修改和替换都不会影响,不过需要from copy import deepcopy

  • 3、dict.fromkeys([key1,key2...] , value = None):创建一个值相同的字典,可以指定特定的值,默认为None

  • 4、dict.get(key, ret = None):方法get为访问字典提供了宽松的环境:访问键为key的值,若存在则返回对应的值;若不存在则返回ret(可指定特定的值,默认为None)

    dict.setdefault(key, ret = None):与get类似,获取指定键的关联值,若不存在则返回ret,并且将ret作为值插入字典

    d = {}
    print(d.setdefault('lhz',1234))
    print(d)
    # {'lhz': 1234}weg
    print(d.setdefault('lhz',None))
    # 1234 若key有对应的值,则返回值
    
  • 5、dict.items():items方法返回一个包含所有字典项的列表,表中每个元素都为(key,value)的形式,排列顺序不定

    返回值属于一种名为字典视图的特殊类型,字典视图可以用于迭代而且可以确定其长度以及进行成员资格审查。字典视图的一个优点是不复制,始终指向字典,随字典改变而变化

  • 6、dict.pop(key):获取指定的键值对,并将其从字典中删除

  • 7、dict.popitem():从字典中弹出一个字典项,并以元组的形式返回,默认为字典项的最后一个(Python3.8.6,python 3.7以前为删除一个随机项)

  • 8、dict.update():使用一个字典中的项更新另一个字典,对于参数提供的字典,将 其添加到当前字典中。若字典包含相同键相同的项,则替换它(返回值为None)


第四章:条件、循环及其他语句

Part 1:再谈print和import
  • 1、合并文本的变量值:print(str1 + str2)直接在print内运算

  • 2、import的几种方式:

    import somemodule
    from somemodule import fun1,fun2,fun3
    from somemodule import * # 导入模块中的所有内容
    
    # 可以给导入的模块起别名
    >>> import math as hello
    >>> hello.sqrt(4)
    2.0
    
Part 2:赋值语句
  • 1、序列解包:将一个序列解包,并将得到的值储存到一系列变量中

    # 实现并行赋值
    >>> x,y,z = 1,2,3 
    >>> print(x,y,z)
    1 2 3
    
    # 还能交换多个变量的值
    >>> x,y = y,x
    >>>print(x,y,z)
    2 1 3
    

    实际上上面的操作是对元组的解包,函数可以通过返回一个元组来实现返回多个值的操作,不过打包序列包含的元素个数必须要和等号左边的目标个数相同

    # 也可以用 * 来收集多余的值,这样无需保证等号左右两边元素个数相同
    # 收集的元素会以列表的形式放在*后的变量名中
    >>> a,b,*rest = [1,2,3,4,5]
    >>> rest
    [3,4,5]
    
    # 也可以将*放在其他位置
    >>> a,*rest,b = [1,2,3,4,5]
    >>> a,b,rest
    (1, 5, [2, 3, 4])
    
  • 2、链式赋值:将多个变量关联到同一个值x = y =fun1()

Part 3:条件语句与布尔值
  • 1、用作布尔表达式时,下面第一行的值都将被解释器视为假,而除此之外的所有值都被视为真

    # False None 0 "" () {} []
    # 要注意,被视为假和是假本身是不一样的
    >>> print(False != (),False != 0,False != {},False != [],False != "")
    True False True True True
    # 输出结果可知,0 == False,其他的对象仅在判断时被视为False
    

    题外话:print(repr(input()),end='#')直接回车输出结果为''#,即不输入便为空字符串

  • 2、startswith()endswith():函数判断一个文本是否以某个(或者几个)字符开始(或者结尾),结果返回bool值

  • 3、三目运算符python版:x = value1 if expression else value2若表达式成立则x为value1,否则为value2

  • 4、相同与相等:x is y来判断x与y是否为同一个对象;x == y来判断是否相等

    注意相同不等于相等,lis1 = [1,2,3];lis2 = [1,2,3]此时lis1和lis2相等不相同

  • 5、链式比较:可以同时使用多个比较运算符,如0 < age < 100

    短路逻辑:当涉及布尔运算符时,python只做必要的运算,如x or y先判断x过后,部分情况下能由x直接判断出结果,此时不会再检查y

    name = input("please enter your name:") or ''若没有输入则名字内容为unknown

  • 6、断言:让程序出现错误条件时立即崩溃

    >>> age = 10
    >>> assert 0 < age < 100 # 正常运行
    >>> age = -1
    >>> assert 0 < age < 100 # 引发异常
    Traceback (most recent call last):
      File "c:\", line 127, in <module>
        assert 0 < age < 100
    AssertionError
    >>> age = -1
    # 还可以在条件后面添加一个字符串,对断言做出说明
    >>> assert 0 < age < 100,'the age must be realistic'
    Traceback (most recent call last):
      File "c:\", line 127, in <module>
        assert 0 < age < 100
    AssertionError:'the age must be realistic'
    
Part 4:循环与迭代
  • 1、可迭代对象是可使用for循环进行遍历的对象,range函数返回一个可迭代对象

  • 2、并行迭代zip:一个内置函数,将多个个序列缝合起来,并返回一个由元组组成的序列。返回值是一个适合迭代的对象,要查看内容可以进行强制转换

    lis1 = ['123','234','345']
    lis2 = [123,234,345]
    print(list(zip(lis1,lis2)))
    # [('123', 123), ('234', 234), ('345', 345)]
    
    # 也能缝合长度不同的序列,缝合后的长度取决于两者中最短的一个
    print(list(zip(range(5),range(100000))))
    # [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
    
  • 3、迭代时获取索引enumerate:内置函数,迭代对象序列的同时获取当前对象的索引,返回一个可迭代的枚举对象

    # 替换字符串列表中所有包含子串'xxx'的字符串
    for index,string in enumerate(string):
        if 'xxx' in string:
            string[index] = '[censored]'  # 替换
    
  • 4、反向迭代和排序后再迭代reversed、sorted:sorted排序后迭代一个序列或可迭代对象,返回一个列表;reversed反向迭代一个序列或可迭代对象,返回一个可迭代的reversed对象

    print(reversed(['a','B','c']))
    print(list(reversed(['a','B','c'])))
    # 
    # ['c', 'B', 'a'] 
    
  • 5、循环中的else:for循环过程中没有发生break,则执行紧随其后的else语句,可以用来判断循环是否提前结束

    for i in range(5):
        pass
    else:
        print("hello world!")
    

    循环举例:(常见技巧)

    while True:
    	word = input()
    	if not word:break
    	# do something here
    	print('The word is:'word)
    
  • 推导式拓展:

    # 推导式可以支持两个变量和两个for
    list1 = [(x,y) for x in range(3) for y in range(3)]
    # [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
    
    # 也可以用来生成字典
    dict1 = {i:'{}^2 is {}'.format(i,i**2) for i in range(3)}
    print(dict1)
    # {0: '0^2 is 0', 1: '1^2 is 1', 2: '2^2 is 4'}
    
Part 5:del、exec与eval
  • 1、del语句:对于不再使用的对象,python通常会将其删除。如下,当lis1和lis2都改变指向时,列表[1,2,3,4]就漂浮在计算机中,没有任何名称与之关联,无法再使用,python解释器会将其删除,该过程被称为垃圾收集。另一种方法,也可以通过del语句进行删除。

    lis1 = [1,2,3,4];lis2 = lis1
    lis1 = None;print(lis1,lis2)
    lis2 = None;print(lis1,lis2)
    # None [1, 2, 3, 4]
    # None None
    
    dic1 = {'123':123}
    del dic1;print(dic1)
    # Traceback (most recent call last):
    #   File "C:\", line 149, in 
    #     del dic1;print(dic1)
    # NameError: name 'dic1' is not defined
    
  • 2、exec语句:将字符串最为代码执行,可以为其指定一个字典作为专门的命名空间,防止影响原本的命名空间。

    exec("print('hello world!')")
    # hello world!
    
    from math import sqrt
    exec('sqrt = 1') # 命名空间相互影响导致sqrt无法正常调用
    sqrt(4)
    # Traceback (most recent call last):
    #   File "D:\学习资料\Python\test.py", line 155, in 
    #     sqrt(4)
    # TypeError: 'int' object is not callable
    
    from math import sqrt
    scope = {}
    exec('sqrt = 1',scope)
    print(sqrt(4),scope['sqrt'])
    # 2.0 1
    
  • 3、eval语句:计算字符串表示的python表达式的值并返回元组,可以据此创建一个计算器,也可以指定一个字典作为其专门的命名空间

    eval(input()) # 6+18*2
    # 42
    

第五章:函数

Part 1:自定义函数
  • 1、计算斐波那契数列的一种方法:

    fibs = [0,1];num = int(input())
    for i in range(num-2):
        fibs.append(fibs[-2]+fibs[-1])  # 很巧妙
    
  • 2、一般而言,要判断某个对象是否可以调用,可以使用内置函数callable()

  • 3、给函数编写文档:放在函数开头的字符串叫做文档字符串,将作为函数的一部分储存起来

    def fun():
        'function doc'
        pass
    
  • 4、所有函数都有返回值,若不人为定义则返回None

    不要让默认的返回值带来麻烦,如果在if语句中返回值,务必确保其他分支也有返回值,否则将引起意外的问题

  • 5、lambda函数:lambda 参数表: 表达式,函数返回值为表达式的值

Part 2:函数的参数

从左到右参数位置:位置参数 - > 关键字参数

  • 1、收集参数:*会收集位置参数(与位置有关的参数),收集后放入元组中;**会收集关键字参数,收集后放入字典,是指定类型的参数而不是指定类型,字典也可能是位置参数!

  • 2、分配参数:*将元组进行解包,解包为位置参数;**将字典进行解包,解包为关键字参数

    只有在定义函数(设计参数)和调用函数(传入参数)时,星号才能起作用。传参时将字典或元组解包为对应类型的参数,接收参数时将多个参数打包为元组或者字典,无需过多考虑参数个数的问题。

Part 3:作用域
  • 1、看不见的字典,叫做命名空间作用域,除了全局作用域以外,每一个函数都有一个局部作用域

    可以通过vars()函数来访问作用域,函数返回一个字典,可以以变量名为键访问变量对应的值

    一般而言,不能修改vars()返回的字典,按照官方文本所说,这样的结果是不可预测的

  • 2、globals访问全局变量,函数globals()类似于vars()返回全局作用域(6包含全局变量的字典),同理locals返回一个包含局部变量的字典。重新关联全局变量时直接用global

    x = 1
    def fun():
        global x
        x += 1
        print(x)
    fun()
    # 2
    
  • 3、作用域嵌套:闭包,函数嵌套,外层函数返回内层函数,返回的函数能够访问其定义所在的作用域

    def outer(x):
        num = 0
        def inner(y):
            nonlocal num  # 注释掉此行和下一行输出结果为num is 0
            num = 99
            print(f"num is {num}")
            return x**y
        return inner
    print(outer(2)(3))
    # num is 99
    # 8
    

    通常,不能给外部作用域的变量赋值,如果一定这么做,可以使用nonlocal,与global用法相同,使得可以给外部作用域(非全局作用域)内的变量赋值

  • 其他函数:

    函数 描述
    map(func,seq[,seq,....]) 对序列中的所有元素执行函数
    filter(func,seq) 返回一个列表,其中包含对其执行函数时结果为真的所有元素
    reduce(func,seq[,initial]) 等价于func(func(func(seq[0],seq[1]),seq[2]),...)
    apply(func[,args[,kwargs]]) 调用函数,同时提供参数给函数
    sum(iterable[,start] 对可迭代对象进行求和后加上start,start为相加的数,默认为0

    前面两个函数都能用推导式进行替换,所以可以尽量使用推导式,增加程序可读性


第六章:抽象

# 类的定义
class ClassName(object): # 所有类都隐式继承了object或者将__metaclass__设置为type
	"""docstring for ClassName"""
	def __init__(self, arg):
		super(ClassName, self).__init__()
		self.arg = arg
  • 1、class语句将会创建独立的命名空间

  • 2、self总是指向对象本身,所以习惯上将其命名为self

  • 3、若foo是一个Person实例,可以将foo.greet()视为Person.greet(foo)

  • 4、私有属性名称以两个下划线开头,私有属性不能从对象外部访问,只能通过存取器方法(如get_name和set_name)

    python内部处理方法:在类定义中,对所有以两个下划线开头的名称都进行转换即在开头加上一个下划线和一个类名,如Person._Person__privateMethod()。可以用这样的处理机制从类外访问私有方法,但是并不应该这么做。

  • 5、如果不希望名称被修改,且做出不要从外部修改属性或方法的标志,可以用一个下划线开头。如from module import *不会导以一个下划线开头的名称

  • 6、内置方法issubclassisinstance:前者确定一个类是否是另一个类的子类,后者确定对象是否是特定类的实例

    class A:
        pass
    class B(A):
        pass
    a = A();b = B()
    print(issubclass(B,A),isinstance(a,B),B.__bases__)
    # True False (,)
    

    如果有一个类,想知道他的基类,可以访问其特殊属性class.__bases__,想要知道一个对象属于哪个类,可以用属性object.__class__

  • 7、多继承时必须注意基类的排列顺序,位于前面的类方法将会覆盖位于后面的类的方法

    多个超类的超类相同时,查找特定方法或属性时的访问超类的顺序称为方法解析顺序(MRO)

  • 8、抽象类:抽象类即包含抽象方法(在子类中必须实现的方法)的类,其最重要的特征是不能实例化

    from abc import ABC,abstractmethod
    class Talker(ABC):
        @abstractmethod # 将talk方法标记为抽象的
        def talk(self):
            pass
        
    class Knigget(Talker): 
        # 该子类没有重写talk进行实现,所以该类也是抽象类,不能实例化
        pass
    print(Knigget())
    # Traceback (most recent call last):
    #   File "C:\", line 199, in 
    #     print(Knigget())
    # TypeError: Can't instantiate abstract class Knigget with abstract methods talk
    class Knigget1(Talker):
        def talk(self):
            print('hello')
    # 对talk实现以后,可以对该类进行实例化,且实例化对象也是抽象类的对象
    print(isinstance(Knigget(),Talker))
    # True
    

    可以通过Talker.register(class1)将其他类注册为Talker,使得所有的class1对象都被视为Talker对象,能够通过isinstanceissubclass的检查,但是class1内部不存在对应的方法,因此容易出现各种问题。所以使用isinstanceissubclass

  • 9、基本概念总结:
    1

  • 其他函数:

    函数 描述
    callable(object) 判断对象是否是可以调用的(如是否是函数或者方法)
    getattr(object,name[,default]) 获取属性的值,还可以提供默认的返回值
    hasattr(object,name) 确定对象是否有指定的属性
    random.choice(sequence) 从一个非空序列中随机地选择一个元素
    setattr(object,name,value) 将对象的指定属性设置为指定的值
    # getattr函数的使用方法
    >>>class A(object):
    ...     bar = 1
    ... 
    >>> a = A()
    >>> getattr(a, 'bar')  # 获取属性 bar 值
    1
    >>> getattr(a, 'bar2') # 属性 bar2 不存在,触发异常(不存在属性且未指定默认值)
    Traceback (most recent call last):
      File "", line 1, in <module>
    AttributeError: 'A' object has no attribute 'bar2'
    >>> getattr(a, 'bar2', 3)    # 属性 bar2 不存在,但设置了默认值
    3
    

第七章:异常

  • 1、Python使用异常对象来表示异常状态,并在遇到错误时引发异常。异常对象未被处理或捕获时,程序将会终止并显示一条错误信息(traceback)

  • 2、raise语句:用来引发异常,并将一个类(必须是Exception的子类)或实例作为参数。以类作为参数时,将自动创建一个实例。

    >>> raise Exception("hyperdrive overload")
    Traceback (most recent call last):
      File "", line 1, in <module>
        raise exception("hyperdrive overload")
    Exception: hyperdrive overload
    
  • 3、自定义异常:像创建类一样,直接或间接地继承Exception(这意味着从任何内置异常类派生都可以),如下

    class SomeCustomException(Exception):
        pass
    
  • 4、捕获异常:try/except语句,引发异常之后跳转到except语句,执行语句内的内容,不再报错。但是可以通过不带参数的raise重新引发异常

    try:
        x,y = map(int,input().split(' '))# 输入10,0
        print(x/y)
    except ZeroDivisionError:
        print("The second number can't be zero!")
        raise # 捕获异常之后还能重新引发,此时不需要提供任何参数
        
    # 举例应用
    class MuffledCalculator: 
    	muffled = False 
    	def calc(self, expr): 
    	try: 
    		return eval(expr) 
    	except ZeroDivisionError: 
    		if self.muffled: 
    			print('Division by zero is illegal') # 打开抑制功能不引发异常
    		else: 
     			raise # 关闭抑制功能重新引发异常
    
    >>> calculator.calc('10 / 0') # 关闭了抑制功能
    Traceback (most recent call last): File "", line 1, in ? 
     	File "MuffledCalculator.py", line 6, in calc 
     		return eval(expr) 
     	File "", line 0, in ? 
    ZeroDivisionError: integer division or modulo by zero 
    
    >>> calculator.muffled = True  # 打开抑制功能
    >>> calculator.calc('10 / 0') 
    Division by zero is illegal 
    
  • 5、处理异常时引发另一个异常:可以使用raise ... from ...来提供上下文,也可以用None来禁用上下文

    try:
        x,y = map(int,input().split(' '))# 输入10,0
        # if y == 0:raise NoneError
        print(x/y)
    except ZeroDivisionError:
        raise NameError('there is an ERROR') #from None
    '''
    Traceback (most recent call last):
      File "D:/学习资料/Python/test.py", line 229, in 
        print(x/y)
    ZeroDivisionError: division by zero
    
    During handling of the above exception, another exception occurred:
    在处理上述异常时,又发生了另一个异常:
    Traceback (most recent call last):
      File "D:/学习资料/Python/test.py", line 231, in 
        raise NameError('there is an ERROR') #from None
    NameError: there is an ERROR
    '''
    
    # 若上面为raise NameError('there is an ERROR') from ZeroDivisionError则输出结果如下
    '''
    ZeroDivisionError # 上下文消息消失
    
    The above exception was the direct cause of the following exception:
    上述异常是下列异常的直接原因:
    Traceback (most recent call last):
      File "D:/学习资料/Python/test.py", line 231, in 
        raise NameError('there is an ERROR') from ZeroDivisionError
    NameError: there is an ERROR
    '''
    

    如上例:使用raise A from B时,python会把A异常的__cause__属性设置为B异常,同时设置A的__supress_context__为True,从而忽略B的__context__属性,不打印B的异常上下文信息

  • 6、多个except语句、else和finally:可以创建多个except语句捕捉不同的异常,从而做出不同反应

    while True:
    	try:
            ...
        except Error1:
            ...
        except (Error2,Error3) as e: 
            # 括号非常重要,否则会引起一些错误,可以打印e来输出异常信息
            ...
        except: # 不属于上述三种异常的将会被此段代码捕获
            ...
        else: 		# 没有出现异常时,执行else(有except语句时才能有else)
            break 	# 没有发生异常时跳出循环
        finally: # 与try语句配套,无论try中有无异常都将执行finally
            ...
    
  • 7、异常与函数:异常在函数中发生之后会从调用处回溯到异常发生的位置

    如果不处理函数中引发的异常,它将向上传播到调用函数的地方。如果在那里也未得到处理,异常将继续传播,直至到达主程序(全局作用域)。如果主程序 中也没有异常处理程序,程序将终止并显示栈跟踪消息。

  • 8、如果只想发出警告,可以使用模块warnings中的函数warn

    >>> from warnings import warn
    >>> warn("i've got a bad feeling about this.")
    Warning (from warnings module):
      File "", line 1
    UserWarning: i've got a bad feeling about this.
    # 警告只显示一次
    # 可以使用warnings中的函数filter函数抑制发出的警告并指定采取的措施:"error"或"ignore"
    >>> from warnings import filterwarnings
    >>> filterwarnings("ignore")
    >>> warn("Something wrong") # 无事发生
    >>>	filterwarnings("error")
    >>> warn("somthing error") # 引发异常
    Traceback (most recent call last):
      File "", line 1, in <module>
        warn('something error')
    UserWarning: something error
    
    # 也可以指定引发异常的类别(即警告类别),但是必须是Warning的子类
    # 使用“error”将警告转换为错误时,将使用指定的异常
    >>> filterwarnings("error") 
    >>> warn("This function is really old...", DeprecationWarning) # 指定异常类型与警告信息
    Traceback (most recent call last): 
     File "", line 1, in <module> 
    DeprecationWarning: This function is really old... 
    
    # 利用filterwarnings可以过滤特定类型的警告(category n.类型)
    >>> filterwarnings("ignore", category=DeprecationWarning) 
    >>> warn("Another deprecation warning.", DeprecationWarning)# 指定异常类型但被过滤
    >>> warn("Something else.") # 未指定异常类型,使用默认的异常并发出警告信息
    Traceback (most recent call last): 
     File "", line 1, in <module> 
    UserWarning: Something else. 
    
  • 总结:python相对与if/else来说更偏向于使用try/except,直接去做,有问题再处理,而不是预先做大量的检查

    函数 描述
    warnings.filterwarnings(action,category=Warning,...) 过滤警告,action为"error"或"ignore"
    warnings.warn(message,category=None) 发出警告,message为警告信息

第八章:特殊方法、特性和迭代器

Part 1:构造函数
  • 1、构造函数__init__不同于普通函数,会在对象创建后自动调用,用于初始化新建对象的状态

    构造函数对应的是析构函数__del__,该方法在对象被销毁(作为垃圾被收集)前调用

    由于无法知道准确的调用时间,建议尽可能不要使用__del__

  • 2、每个类都有一个或多个超类,并从它们那里继承行为。对类B的实例调用方法或属性时,如果找不到该方法或属性,将在其超类A中查找。子类重写超类方法,则优先调用重写后的方法。

    尝试重写构造函数时,必须调用超类的构造函数,否则可能无法正确地初始化对象

    • 确保超类执行基本的初始化有以下两种方法:

      1、调用未关联的超类构造函数(旧版python):对实例调用方法时,方法的参数self将自动关联到实例(称为关联的方法),未关联方法即直接通过类名调用类方法

      class A:
          def __init__(self):
              self.name = 'class A'
          def myprint(self):
              print(self.name)
      class B(A):
          def __init__(self):
              A.__init__(self)# 此行注释掉程序报错
              # 此时超类A的构造函数未关联对象,调用时将对参数属性进行初始化
              self.age = '20'
      b = B()
      b.myprint() # class A
      

      2、使用函数super(新版python):调用super函数时,将当前类和当前对象作为参数,对其返回的对象调用方法时,调用的是超类的方法而不是当前类的方法

      # 1中代码第8行换为以下代码
      super(B,self).__init__()# super的括号内可以不提供任何参数
      

      super函数返回的实际上时一个super对象,这个对象负责执行方法解析。当访问它的属性时,它将在所有的超类中进行查找,直到找到指定的属性或引发AttributeError异常

Part 2:元素访问与特殊方法
  • 1、在Python中,协议通常指的是规范行为的规则,类似于接口。协议指定应该实现哪些方法以及这些方法应该做些什么。

  • 2、基本的序列和映射协议:序列和映射基本上是元素的集合,要实现他们的基本行为(协议),不可变对象需要实现2个方法,而可变对象需要实现4个。要创建自定义的序列或映射,必须实现序列和协议指定的所有方法。2

    对于上述方法,还有一些额外的要求:

    1、对于序列,如果键为负整数,应从末尾往前数。换而言之,x[-n]应与x[len(x)-n]等效。

    2、如果键的类型不合适(如对序列使用字符串键),可能引发TypeError异常。

    3、对于序列,如果索引的类型是正确的,但不在允许的范围内,应引发IndexError异常。

    # 创建一个无穷的算术序列(没有__len__)
    def check_index(key):
        """
        指定的键是否可以索引?
        1、键必须为非负整数
        2、若非整数,引发TypeError;若为负数,引发IndexError
        """
        if not isinstance(key, int): raise TypeError('The index must be an integer!')
        if key < 0: raise IndexError('The index must be a non-negative number!')
    
    class Arithmetic_Sequence:
        def __init__(self, start=0, step=1):
            """
            初始化算数序列,满足递推公式而且可以修改其中的值
            start:序列的第一个值
            step:序列相邻两元素的差值
            changed:记录修改过的值
            """
            self.start = start
            self.step = step
            self.changed = {}
    
        def __getitem__(self, key):  # 对实例的下标访问会自动转换为调用__getitem__方法
    
            check_index(key)  # 检查索引
    
            try:
                return self.changed[key]  # 查询是否存在对应的值,没有则引发KeyError
            except KeyError:
                return self.start + key * self.step  # 根据递推公式计算值
    
        def __setitem__(self, key, value):  # 对实例设置值自动转换为__setitem__方法
    
            check_index(key)
    
            self.changed[key] = value  # 储存修改后的值
    
    obj = Arithmetic_Sequence(1,2) # start为1,step为2
    print(obj[10],obj[20]) # 21 41
    # print(obj['lhz'],obj[-99]) # 报错正常
    obj[10] = 999;print(obj[10]) # 设置值正常
    del obj[10] # AttributeError: __delitem__ 没有对应的方法,无法删除
    
  • 3、从listdictstr派生:从哪一类派生,则该类的对象就为对应的类别,下举list特例

    # 实现一个带访问计数器的列表
    class CounterList(list):
        def __init__(self,*args):
            super().__init__(*args)
            self.count = 0
        def __getitem__(self,index):
            self.count += 1
            return super().__getitem__(index)
    c1 = CounterList(range(10))
    print(c1) # 继承list类则该类对象为list类型
    print(c1,c1.reverse())# 此行可以看出同一行输出函数先调用再输出
    del c1[0:3]
    print(c1.count,c1[0]+c1[1]+c1[1],c1.count) # 继承了list中的默认方法
    
    # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] None
    # 0 16 3
    
Part 3:特性 property
  • 1、函数property:可以将新的属性关联到方法和已知属性,通过存取方法来定义属性,可以在存取方法内对新属性进行一些限定,直接定义则无法对新属性限定

    class Rectangle: 
        def __init__(self): 
            self.width = 0
            self.height = 0 
        def set_size(self, size):
            self.width,self.height = size
        def get_size(self):
            return self.width, self.height
        size = property(get_size,set_size)
    >>> r = Rectangle() 
    >>> r.width = 10 
    >>> r.height = 5 
    >>> r.size 
    (10, 5) 
    >>> r.size = 150, 100 # 设置时调用对应的方法,size属性依然受制于width和height
    >>> r.width 
    150
    # 以下为官方文档3.8.6
    """
    Property attribute.
    
    fget:function to be used for getting an attribute value
    fset:function to be used for setting an attribute value
    fdel:function to be used for del'ing an attribute
    doc :docstring
    
    Typical use is to define a managed attribute x:
    
        class C(object):
            def getx(self): return self._x
            def setx(self, value): self._x = value
            def delx(self): del self._x
            x = property(getx, setx, delx, "I'm the 'x' property.")
    
    Decorators make defining new properties or modifying existing ones easy:
    """
    
  • 2、静态方法和类方法:将它们分别包装在staticmethod和classmethod类的对象中,静态方法中没有参数self,可以直接通过类来调用。类方法的定义中包含类似于self的参数,通常被命名为cls。对于类方法,也可以通过对象直接调用,但参数cls将自动关联到类。

    class  MyClass:
        def smeth():
            print('this is a static method')
        smeth = staticmethod(smeth)
        def cmeth(cls):
            print('this is a class method of',cls)
        cmeth = classmethod(cmeth)
    # 像上面两种包装和替换的方法比较繁琐,在python2.4中引入了装饰器
    # 可以指定一个或多个装饰器,可以用@列出多个装饰器
    # 指定多个装饰器时,应用的顺序和列出的顺序相反
    class Myclass:
        @staticmethod
        def smeth():
            print('this is a static method')
        @classmethod
        def cmeth(cls):
            print('this is a class method')
    
  • 3、__getattr__、__setattr__等特殊方法

    ​ 该类特殊方法会拦截对对象的所有访问企图,其用途之一是在旧式类中实现特性property(在旧式类中,property的行为可能不符合预期)。要在属性被访问时执行一段代码,必须使用一些特殊方法。

    '''四类特殊方法(旧式类中,只需要使用后面三个)
    __getattribute__(self,name):在属性被访问时自动调用(只适用于新式类)
    __getattr__(self,name):在属性被访问而对象没有这样的属性时自动调用(属性查找失败时调用,兜底)
    __setattr__(self,name,value):试图给属性赋值时自动调用
    __delattr__(self,name):试图删除属性时自动调用
    '''
    # 举例1:特殊方法的应用
    class Rectangle: # 版本2,版本1见Part 3,1
    	def __init__ (self): 
    		self.width = 0 
    		self.height = 0 
    	def __setattr__(self, name, value): 
    		if name == 'size': 
    			self.width, self.height = value 
         	else: 
                 self. __dict__[name] = value 
    	def __getattr__(self, name): 
    		if name == 'size': 
                 return self.width, self.height 
             else: 
                 raise AttributeError()
    '''
     这个版本的需要考虑更多的细节。
     1、如果涉及的属性不是size也会调用方法__setattr__。因此这个方法必须考虑size和非size的情况:如果涉及到size则执行与以前相同的操作;若为新的属性就执行特殊属性__dict__,该属性是一个包含所有的实例属性的字典。不能执行常规的属性赋值,因为会反复调用__setattr__进入无限循环。
     2、仅当没有找到指定属性时,才会调用方法__getattr__,即如果属性不是size则会引发AttributeError异常
    '''
    # 举例2:__getattr__方法,像访问属性一样访问字典
    class Dict(dict):
        def __init__(self, *args, **kwargs):# init初始化对象自动转换为__setattr__调用
           super(Dict, self).__init__(*args, **kwargs)
    
        def __getattr__(self, name):
            value =  self[name]
            if isinstance(value, dict):
                value = Dict(value)
            return value
    obj = Dict({'lhz':1234,'attr':{'age':20,'sex':'man'}})
    # lhz传入时被转换为str类型,若字典键为Number则obj.key不可用
    print(obj, obj['lhz'] is obj.lhz, sep='\n') 
    print(obj.attr)
    # {'lhz': 1234, 'attr': {'age': 20, 'sex': 'man'}}
    # True
    # {'age': 20, 'sex': 'man'}
    

    **注意:**编写方法__setattr__时要注意避开无限循环,编写__getattribute__时也应注意。该特殊方法拦截对所有属性的访问,因此将拦截对__dict__的访问!唯一安全的方式是使用超类的__getattribute__(使用super方法)

Part 4:迭代器和生成器
  • 1、迭代器协议:for循环可以迭代实现__iter__方法的对象,该方法返回一个迭代器,是包含__next__方法的对象,调用该方法可以不提供任何参数。当调用方法__next__时,迭代器应返回其下一个值,如果无值可以返回,应该引发StopIteration异常。还可以使用内置的函数next,这种情况下next(it)it.__next__()等效。

    # 斐波那契数列的迭代器方法:
    class Fibs:
        def __init__(self):
            self.a = 0
            self.b = 1
        def __next__(self): # 实现__next__才是迭代器
            self.a,self.b = self.b,self.a+self.b
            return self.a
        def __iter__(self): # 无__iter__不可迭代
            return self # 返回迭代器本身,也可以是其他迭代器,python根据返回迭代器的类型去找对应的__next__并执行
    # 一般情况下,都在另一个对象中实现__iter__,并在for循环中使用这个对象
    # 但是推荐在迭代器中也实现方法__iter__并返回self,这样迭代器就可以直接用于for循环中
    # 例:for i in Fibs():
    # for循环会先检查__iter__,然后进入__next__依次执行,每次返回一个值赋给i
    
    # 通过对可迭代对象调用内置函数iter,可以获得一个迭代器
    >>> it = iter([1,2,3])
    >>> next(it)
    1
    >>> next(it)
    2
    
  • 2、迭代器创建序列:将返回的可迭代对象直接转换为list类型

    class TestIterator:
        value = 0
        def __next__(self):
            self.value += 1
            if self.value > 10:raise StopIteration # 遇到StopIteration停止迭代
            return self.value
        def __iter__(self):
            return self
    print(list(TestIterator()))
    # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    
  • 3、生成器:包含yield语句的函数都被称为生成器,生成器被调用时不会执行函数体内的代码,而是返回一个迭代器。每次使用yield生成一个值(类似于return)之后,函数将冻结,等待被唤醒。被重新唤醒后,从停止的地方开始继续执行。

    # 生成器由两个单独的部分组成:生成器函数和生成器的迭代器
    # 1、生成器函数是由def定义的,其中包含yield
    # 2、生成器的迭代器是这个函数返回的结果,可以像其他迭代器一样使用
    # 以上两个实体通常被视为一个,统称为生成器
    def fun():
        for i in range(2):
        	print(i)
        	yield i 
        	print('***')
    print(type(fun()))
    for i in fun():
        print('===')
    '''
    
    0
    ===
    ***
    1
    === # 此时遍历没有结束,最后一次遍历输出最后一个'***'
    ***
    '''
    # 生成器遍历列表
    def flatten(nested):
        for sublist in nested:
            for element in sublist:
                yield element
    

    生成器推导:工作原理与列表推导相同,但不是创建一个列表,而是返回一个生成器,使其能够逐步执行计算g = ((i+2)**2 for i in range(2,10))(使用next进行迭代)

    # 也可以这样
    sum( i**2 for i in range(10))
    
  • 4、递归式生成器:处理任意层嵌套的列表

    def flatten(nested):
        try:
            for sublist in nested: # nested为一个元素则引发TypeError
                for element in flatten(sublist):
                    yield element
        except TypeError:
            yield nested
    # 该方案存在问题是,无法处理字符串为元素的序列
    # 依次检查字符串每个元素时会陷入无穷递归,字符串的第一个元素也是字符串
    # 长度为1的字符串的第一个元素是它本身
    # 完善后如下:
    def flatten(nested):
        try:
            try: nested+''
            except TypeError:pass # 忽略由字符串非法相加引起的异常
            else: raise TypeError
    
            for sublist in nested:
                for element in flatten(sublist):
                    yield element
        except:
            yield nested
    
  • 5、生成器方法:生成器开始运行后,可以使用生成器和外部之间的通信渠道来提供值。在挂起的生成器内部,yield可能用作表达式,即生成器重新运行时,yield返回一个通过send函数从外部发送的值。如果使用next,yield将返回None

    def gen(info):
        while True:
            new = (yield info)
            print(new)
    g = gen('hello')
    print(g.send(None)) # 第一次启动时发送info参数必须为None
    print(g.send('world'))
    '''
    hello # 第6行第一次yield输出info后挂起
    world # 第7行从上次yield开始,输出信息'world'
    hello # 再次进入循环输出info
    '''
    # 由上可知,仅当生成器被挂起(遇到第一个yield)后使用send才有意义
    # send参数为None表示没有
    

    方法throw:用在生成器中(yield处)引发异常,调用时可以提供一个异常类型、一个可选值和一个traceback对象

    方法close:用于停止生成器,调用时无需提供任何参数(用时补充)、

  • 补充:

    函数 描述
    iter(obj) 从可迭代对象创建一个迭代器
    super(class,obj) 返回一个超类的关联实例

你可能感兴趣的:(python笔记,python)