写在前面:
本文是根据[挪] Magnus Lie Hetland的《Python基础教程(第三版)》总结的部分个人笔记
电子版资料想要的可以私信滴滴我哦
如有错误欢迎交流指出!
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、字符串表示str
和repr
:
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
是用于处理复数的模块,不同库的同名函数注意调用时区分
**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不是列表方法
**1、元组:**元组也是序列,与列表的唯一差别时元组不能被修改,单元素元组末尾必须加逗号
**2、元组的作用:**元组作为映射中的键(以及集合的成员),而列表不行;Python中部分内置函数和方法返回元组
集合内元素不可变,因此列表不能为集合的成员
补充:
函数 | 描述 |
---|---|
len(seq) |
返回序列的长度 |
max(args) |
返回序列或一组参数中的最大值(可以为多个非序列的参数) |
min(args) |
返回序列或一组参数中的最小值(同上) |
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
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
将字符串格式设置应用于字典(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)
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
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()
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'
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'}
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
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 参数表: 表达式
,函数返回值为表达式的值
从左到右参数位置:位置参数 - > 关键字参数
1、收集参数:*
会收集位置参数(与位置有关的参数),收集后放入元组中;**
会收集关键字参数,收集后放入字典,是指定类型的参数而不是指定类型,字典也可能是位置参数!
2、分配参数:*
将元组进行解包,解包为位置参数;**
将字典进行解包,解包为关键字参数
只有在定义函数(设计参数)和调用函数(传入参数)时,星号才能起作用。传参时将字典或元组解包为对应类型的参数,接收参数时将多个参数打包为元组或者字典,无需过多考虑参数个数的问题。
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、内置方法issubclass
与isinstance
:前者确定一个类是否是另一个类的子类,后者确定对象是否是特定类的实例
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对象,能够通过isinstance
和issubclass
的检查,但是class1内部不存在对应的方法,因此容易出现各种问题。所以使用isinstance
和issubclass
9、基本概念总结:
其他函数:
函数 | 描述 |
---|---|
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为警告信息 |
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
异常
1、在Python中,协议通常指的是规范行为的规则,类似于接口。协议指定应该实现哪些方法以及这些方法应该做些什么。
2、基本的序列和映射协议:序列和映射基本上是元素的集合,要实现他们的基本行为(协议),不可变对象需要实现2个方法,而可变对象需要实现4个。要创建自定义的序列或映射,必须实现序列和协议指定的所有方法。
对于上述方法,还有一些额外的要求:
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、从list
、dict
和str
派生:从哪一类派生,则该类的对象就为对应的类别,下举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
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方法)
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) |
返回一个超类的关联实例 |