一、切片
- 在python的使用中,对于列表、元组的元素取值是非常常见的,例如:
- *
注意:切片是顾头不顾尾的
>>> L = ["aaa","bbb","ccc","ddd"]
>>> print(L)
['aaa', 'bbb', 'ccc', 'ddd']
>>> print(L[0],L[1],L[2])
aaa bbb ccc
>>> for i in range(3):
... a.append(L[i])
...
>>> print(a)
['aaa', 'bbb', 'ccc']
>>> print(L[0:3])
['aaa', 'bbb', 'ccc']
>>> print(L[:3])
['aaa', 'bbb', 'ccc']
>>> L = list(range(100))
>>> print(L)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
>>> print(L[:])
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
>>> print(L[:10])
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> print(L[10:20])
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> print(L[:-1])
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98]
>>> print(L[-10:-1])
[90, 91, 92, 93, 94, 95, 96, 97, 98]
>>> print(L[-20:-10])
[80, 81, 82, 83, 84, 85, 86, 87, 88, 89]
>>> print(L[-10:])
[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
>>> print(L[::5])
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]
- 上面的是列表的取值案例,列表的切片最终输出的结果还是列表
- python的切片还可以引用在其他数据类型上,如元组、字符串等,例如:
>>> T = (1,2,3,4,5)
>>> print(T)
(1, 2, 3, 4, 5)
>>> print(T[1:3])
(2, 3)
>>> print(T[-3:])
(3, 4, 5)
>>> S = "ABCDEFG"
>>> print(S)
ABCDEFG
>>> print(S[4:])
EFG
>>> print(S[::3])
ADG
- 在自己实践过之后可以发现,元组切片后的输出还是元组,同样字符串切片后还是字符串
- 这里引用一个小案例,不使用strip函数,利用切片来去除首位的空格:
def trim(s):
if s[:1] != ' ' and s[-1:] != ' ':
return s
elif s[:1] == ' ':
return trim(s[1:])
else:
return trim(s[:-1])
if trim('hello ') != 'hello':
print('测试失败!')
elif trim(' hello') != 'hello':
print('测试失败!')
elif trim(' hello ') != 'hello':
print('测试失败!')
elif trim(' hello world ') != 'hello world':
print('测试失败!')
elif trim('') != '':
print('测试失败!')
elif trim(' ') != '':
print('测试失败!')
else:
print('测试成功!')
这段代码通过切片去判断首位是否有空格,有的话会再次调用函数,并且引用第1元素或第-1元素来去除空格,然后继续做判断,直至首尾没有空格
二、迭代
- 首先要知道的是,通过for循环来遍历列表、元组、字符串等,这种遍历就称为迭代
- 需要注意的是,python中的迭代是直接通过
for in
的这种方式来完成的,而其他语言,比如C语言中的迭代是通过下标完成的,即i++
这种方式。并且python的for循环还可以使用在其他的可迭代的数据类型上 - 除了列表、元组这种有下标的数据类型,python中的for循环还可以使用在其他没有下标的可迭代数据类型上,如字典:
>>> L = {"aaa":1,"bbb":2,"ccc":3}
{'aaa': 1, 'bbb': 2, 'ccc': 3}
>>> for i in L:
... print(i)
...
aaa
bbb
ccc
>>> for i in L.values():
... print(i)
...
1
2
3
>>> for x,y in L.items():
... print(x,y)
...
aaa 1
bbb 2
ccc 3
要注意的是,因为字典的存储不是按照列表的方式顺序排列的,所以迭代出的结果有可能会不同
- 其他类型的一些循环:
>>> S = "ABCDEFG"
>>> print(S)
ABCDEFG
>>> for i in S:
... print(i)
...
A
B
C
D
E
F
G
>>> T = (1,2,3,4)
>>> for i in T:
... print(i)
...
1
2
3
4
>>> L = [1,2,3,4]
>>> for i in L:
... print(i)
...
1
2
3
4
- 在上面的案例中,可以看出,for循环只要是使用于可迭代的数据类型,都可以正常运行,那怎么看是否是可迭代的数据类型呢,python提供了相应的方法:
>>> from collections.abc import Iterable
>>> isinstance('aaaa',Iterable)
True
>>> isinstance([1,2,3],Iterable)
True
>>> isinstance((1,2,3),Iterable)
True
>>> isinstance(S,Iterable)
True
>>> isinstance(L,Iterable)
True
>>> isinstance(T,Iterable)
True
>>> isinstance({"aaa":1,"bbb":2},Iterable)
True
- 引用其他的一些小案例:
>>> L = [1,2,3,4,5]
>>> for x,y in enumerate(L):
... print(x,y)
...
0 1
1 2
2 3
3 4
4 5
>>> L = [(1,2),(3,4),(5,6)]
>>> for x,y in L:
... print(x,y)
...
1 2
3 4
5 6
def findMinAndMax(L):
if L == []:
return (None,None)
else:
min=L[0]
max=L[0]
for i in L:
if i > max:
max = i
if i < min:
min = i
return (min,max)
if findMinAndMax([]) != (None, None):
print('测试失败!')
elif findMinAndMax([7]) != (7, 7):
print('测试失败!')
elif findMinAndMax([7, 1]) != (1, 7):
print('测试失败!')
elif findMinAndMax([7, 1, 3, 9, 5]) != (1, 9):
print('测试失败!')
else:
print('测试成功!')
先创建一个参照数,然后循环列表依次对比参照数,从而取出最大值和最小值
三、列表推导式
- 列表推导式,也叫列表生成式,(List Comprehensions),是python内置的一个简单、强大的可以用来创建列表的生成式
- 下面举一些简单的例子进行对比:
>>> L = list(range(1,11))
>>> print(L)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> N = []
>>> for i in L:
... N.append(i*i)
...
>>> print(N)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
>>> [i * i for i in L]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
[i * i for i in L]
在我的理解中,其实就是直接操作列表的值,使用for循环去指定要操作的列表,把里面的值拿出来,然后直接在开头对值进行操作,从而达到想要的效果
>>> L = ["Aaa","Bbb","Ccc"]
>>> [i.lower() for i in L]
['aaa', 'bbb', 'ccc']
使用lower函数把字符变成小写
注意:列表推导式是创建一个新列表,不会修改原列表的元素
- 在熟悉基本的列表推导式后,我们还可以使用if语句、多层for循环来创建列表推导式,例如:
>>> L = list(range(11))
>>> print(L)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> [i for i in L if i % 2 == 0]
[0, 2, 4, 6, 8, 10]
可以发现if语句是写在末尾的,如果想要加else呢?就需要换一下位置了
>>> [i if i % 2 == 0 else -i for i in L]
[0, -1, 2, -3, 4, -5, 6, -7, 8, -9, 10]
这个推导式的意思是除以二余零的直接输出,否则变成负数输出
>>> [x + y for x in "abc" for y in "efg" ]
['ae', 'af', 'ag', 'be', 'bf', 'bg', 'ce', 'cf', 'cg']
>>> [x + y + i for x in "abc" for y in "efg" for i in "hij"]
['aeh', 'aei', 'aej', 'afh', 'afi', 'afj', 'agh', 'agi', 'agj', 'beh', 'bei', 'bej', 'bfh', 'bfi', 'bfj', 'bgh', 'bgi', 'bgj', 'ceh', 'cei', 'cej', 'cfh', 'cfi', 'cfj', 'cgh', 'cgi', 'cgj']
但是一般只会用到两层循环,两层以上的就很少用了
注意:在列表推导式中,for后面跟的if是过滤条件,不能跟else否则会报错,而if..else是表达式,所以需要写在前面
- 之前说过for循环只要是可迭代的数据类型都可以使用,那么同样的列表推导式也可以进行引用
>>> d = {"a":1,"b":2,"c":3}
>>> [x + '=' + y for x,y in d.items()]
Traceback (most recent call last):
File "", line 1, in <module>
File "", line 1, in <listcomp>
TypeError: can only concatenate str (not "int") to str
在引用字典时,发现报错了,原来是因为int类型导致的报错
>>> d = {"a":"1","b":"2","c":"3"}
>>> [x + '=' + y for x,y in d.items()]
['a=1', 'b=2', 'c=3']
注意:在使用列表推导式时,要注意列表、元组、字典元素的数据类型,比如要使用lower函数转换大小写时,如果列表中元素有int类型的,那么就会报错,可以使用内建函数isinstance来判断一个变量是不是字符串,例如:
>>> L = ["Aaa","Bbb",111,222,"Ccc",None]
>>> [ i.lower() for i in L]
Traceback (most recent call last):
File "", line 1, in <module>
File "", line 1, in <listcomp>
AttributeError: 'int' object has no attribute 'lower'
这样会报错,所以可以加一个isinstance的过滤条件
>>> [ i.lower() for i in L if isinstance(i,str) == True]
['aaa', 'bbb', 'ccc']
三、生成器(generator)
- 通过上面的列表推导式,我们可以直接创建一个列表,但是列表的容量肯定是有限的,如果要创建一个100万个元素的列表,不仅会占用很大的存储空间,而且,如果说里面的元素有百分之90都用不到的话,那么就会浪费很大的空间,而生成器就可以解决这个问题
- 生成器可以根据某个算法在循环过程中不断的推算后续的元素,这样就不用创建完整的列表,从而节省大量的空间,而这种一边循环一边生成的机制,就叫生成器==(generator)==
- 下面是创建生成器的案例:
>>> L = list(range(11))
>>> [ i * i for i in L]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
>>> ( i * i for i in L)
<generator object <genexpr> at 0x0000022979D4C0B0>
可以发现生成器不会直接输出结果
>>> g = ( i * i for i in L)
>>> g
<generator object <genexpr> at 0x0000022979D4DA80>
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
100
>>> next(g)
Traceback (most recent call last):
File "", line 1, in <module>
StopIteration
>>> g = ( i * i for i in L)
>>> for x in g:
... print(x)
...
0
1
4
9
16
25
36
49
64
81
100
- 从上面的例子可以看出,生成器是可以直接使用for循环调用的,所以说在实际使用生成器时,几乎不会使用next去依次调用
- 下面引用一个案例,实现斐波拉契数列
def fbl(n):
x,y,i = 0,0,1
while x < n:
print(i)
y,i = i,y + i
x = x + 1
return 'done'
fbl(6)
1
1
2
3
5
8
定义x为循环的次数,每次循环会加一,当x等于函数传入的参数n时,while循环就会结束
而y和i在每次循环时,y会赋予i的值,而i会赋予y+i的值,这样实现的效果就是:
1:y=1,x=0+1=1
2:y=1,x=1+1=2
3:y=2,x=1+2=3
4:y=3,x=2+3=5
5:y=5,x=3+5=8
更好理解的就是x2=y1+x1,x3=y2+x2,x4=y3+y3这种规律
- 从上面的案例中可以看出,斐波拉契数列就是根据上一个运算结果推算出下一个运算结果,这就比较像生成器的特性了
- 而上面定义的函数只需要把print(i)改为yield i就可以变成一个生成器:
def fbl(n):
x,y,i = 0,0,1
while x < n:
yield i
y,i = i,y + i
x = x + 1
return 'done'
f = fbl(6)
print(f)
for a in f:
print(a)
<generator object fbl at 0x000001F1B9BED8C0>
1
1
2
3
5
8
这里的函数和上面的普通函数只改变了一个关键字,但是因为这个关键字,使函数执行的流程发生了改变,上面的普通函数就是依次的顺序执行,遇到return语句或是到了最后一行语句就会返回,而这里的函数变成了只有在遇到next函数或for循环时才会在yield关键字的语句位置进行返回,再次使用next或继续for循环时,会从上次yield语句的位置继续执行,直到再次遇到yield语句然后再次进行返回,这里的函数其实就变成了生成器函数
def test():
print('aaa')
yield
print('bbb')
yield
print('ccc')
yield
a = test()
next(a)
print("1")
next(a)
print("2")
next(a)
print("3")
next(a)
aaa
1
bbb
2
ccc
3
Traceback (most recent call last):
File "c:\Users\12488\Desktop\python\pachong.py", line 17, in <module>
next(a)
StopIteration
这里其实就更直观了,在第一次调用时,在第一个yield停下了,从而输出了aaa,第二次从第一个yield位置继续执行在第二个yield的位置停下,从而输出的bbb,第三次从第二个yield的位置继续执行,在第三个的yield位置停下,从而输出了ccc,第四次的时候,因为已经没有yield关键字了,所以就报错了
注意:
1、如果一个函数中包含yield关键字,那么这个函数就会变成生成器函数,而调用一个生成器函数将会返回一个生成器的数据
2、调用生成器函数会创建一个生成器对象,多次调用生成器函数会创建多个相互独立的生成器,示例:
def test():
print('aaa')
yield
print('bbb')
yield
print('ccc')
yield
next(test())
print("1")
next(test())
print("2")
next(test())
print("3")
next(test())
aaa
1
aaa
2
aaa
3
aaa
可以发现每次都是只到第一个yield就停下了,这就是因为只调用了生成器函数,所以每次都是创建新的生成器,而上一个脚本是把生成器函数赋值了,每次调用的是相同的生成器,很明显上一个脚本的写法才是正确的
- 这里引用廖雪峰大神的一个案例:
1、在使用for循环调用生成器时,会发现拿不到生成器return语句的返回值,如果想拿到返回值,必须捕获StopIteration的错误,返回值包含在StopIteration的value值中:
def fbl(n):
x,y,i = 0,0,1
while x < n:
yield i
y,i = i,y + i
x = x + 1
return 'done'
f = fbl(6)
while True:
try:
x = next(f)
print('f:',x)
except StopIteration as e:
print('return value:',e.value)
break
f: 1
f: 1
f: 2
f: 3
f: 5
f: 8
return value: done
2、实现杨辉三角
def triangles():
L = [1]
while True:
yield L
X = [0] + L
Y = L + [0]
L = [X[i] + Y[i] for i in range(len(X))]
n = 0
results = []
for t in triangles():
results.append(t)
n = n + 1
if n == 10:
break
for t in results:
print(t)
if results == [
[1],
[1, 1],
[1, 2, 1],
[1, 3, 3, 1],
[1, 4, 6, 4, 1],
[1, 5, 10, 10, 5, 1],
[1, 6, 15, 20, 15, 6, 1],
[1, 7, 21, 35, 35, 21, 7, 1],
[1, 8, 28, 56, 70, 56, 28, 8, 1],
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
]:
print('测试通过!')
else:
print('测试失败!')
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]
[1, 6, 15, 20, 15, 6, 1]
[1, 7, 21, 35, 35, 21, 7, 1]
[1, 8, 28, 56, 70, 56, 28, 8, 1]
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
测试通过!
def triangles():
L = [1]
while True:
yield L
x = [0] + L
y = L + [0]
L = [x[i] + y[i] for i in range(len(x))]
下面就是每一次循环时的规律,可以看出来每次都是相加、重组的一个流程
L = [1]
x=[0,1],y=[1,0]
range(len(x)) = range(2)
x[0] + y[0] = 0 + 1 = 1
x[1] + y[1] = 1 + 0 = 1
[1,1]
L = [1,1]
x=[0] + [1,1] = [0,1,1]
y=[1,1] + [0] = [1,1,0]
range(len(x)) = range(3)
x[0] + y[0] = 0 + 1 = 1
x[1] + y[1] = 1 + 1 = 2
x[2] + y[2] = 1 + 0 = 1
[1,2,1]
L = [1,2,1]
x=[0] + [1,2,1] = [0,1,2,1]
y=[1,2,1] + [0] = [1,2,1,0]
range(len(x)) = range(4)
x[0] + y[0] = 0 + 1 = 1
x[1] + y[1] = 1 + 2 = 3
x[2] + y[2] = 2 + 1 = 3
x[3] + y[3] = 1 + 0 = 1
[1,3,3,1]
L = [1,3,3,1]
x=[0] + [1,3,3,1] = [0,1,3,3,1]
y=[1,3,3,1] + [0] = [1,3,3,1,0]
range(len(x)) = range(5)
x[0] + y[0] = 0 + 1 = 1
x[1] + y[1] = 1 + 3 = 4
x[2] + y[2] = 3 + 3 = 6
x[3] + y[3] = 3 + 1 = 4
x[4] + y[4] = 1 + 0 = 1
[1,4,6,4,1]
注意:生成器函数的调用会直接返回一个生成器对象,而普通函数会直接返回调用结果
四、迭代器
- 经过上面的学习,可以知道可以直接进行for循环的数据类型有:列表(list),元组(tuple)、字典(dict)、集合(set)、字符串(str)等,这些都是集合数据类型的,还有一个种是生成器(generator),包括了生成器和生成器函数
(带yield关键字的函数)
- for循环可以作用于上述所说的所有数据类型,而这些数据类型又称为
可迭代数据类型(Iterable)
,利用isinstance()
函数可以判断一个数据是否为可迭代的数据类型
>>> from collections.abc import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('aaa', Iterable)
True
>>> isinstance((i for i in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False
- 我们知道生成器不但可以使用for循环,还可以使用
next()
函数不断调用直到抛出StopIteration
错误,而这种可以被next()
函数调用并不断返回下一个值的数据类型,也叫做迭代器(Iterator)
,同样的使用isinstance()
函数可以判断是否为迭代器:
>>> from collections.abc import Iterator
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('aaa', Iterator)
False
>>> isinstance((i for i in range(10)), Iterator)
True
注意:生成器为Iterable而迭代器为Iterator
- 经过上面的案例,可以看出生成器既是可迭代数据类型,也是迭代器数据类型,但是为什么其他的可迭代数据不是迭代器数据类型呢?如列表、字典、字符串等
这是因为python中的迭代器表示的是一个数据流,这个数据流是可以被
next()
函数不断调用并返回下一个值,直到没有数据时报StopIteration
错误。 这也说明了迭代器的计算是惰性的,只有在需要返回下一个数据时才会进行计算,而列表、字典、字符串这些数据在创建的时候,里面的元素就已经定义好了,在需要返回时并不会计算,而是直接返回。 他们的区别还在于迭代器可以表示一个无限大的数据流,例如所有自然数,因为他的计算模式是惰性的,而像列表、字典、字符串这种的,很明显存储的元素是有限的,所以它们不是迭代器的数据类型 - 列表、字典、字符串等虽然不是迭代器的数据类型,但是可以通过函数
iter()
来获取一个迭代器的数据,例如:
>>> from collections.abc import Iterator
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter({}), Iterator)
True
>>> isinstance(iter('aaa'), Iterator)
True
- 最后引入几个知识点:
1、python的for循环本质就是通过不断调用
next()
函数实现的
2、凡是for循环可以调用的类型都是可迭代数据类型
3、凡是可以使用next()
函数不断调用输出结果的数据都是迭代器数据类型
以看出生成器既是可迭代数据类型,也是迭代器数据类型,但是为什么其他的可迭代数据不是迭代器数据类型呢?如列表、字典、字符串等这是因为python中的迭代器表示的是一个数据流,这个数据流是可以被
next()
函数不断调用并返回下一个值,直到没有数据时报StopIteration
错误。
这也说明了迭代器的计算是惰性的,只有在需要返回下一个数据时才会进行计算,而列表、字典、字符串这些数据在创建的时候,里面的元素就已经定义好了,在需要返回时并不会计算,而是直接返回。
他们的区别还在于迭代器可以表示一个无限大的数据流,例如所有自然数,因为他的计算模式是惰性的,而像列表、字典、字符串这种的,很明显存储的元素是有限的,所以它们不是迭代器的数据类型
- 列表、字典、字符串等虽然不是迭代器的数据类型,但是可以通过函数
iter()
来获取一个迭代器的数据,例如:
>>> from collections.abc import Iterator
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter({}), Iterator)
True
>>> isinstance(iter('aaa'), Iterator)
True
- 最后引入几个知识点:
1、python的for循环本质就是通过不断调用
next()
函数实现的
2、凡是for循环可以调用的类型都是可迭代数据类型
3、凡是可以使用next()
函数不断调用输出结果的数据都是迭代器数据类型Original: https://blog.csdn.net/rzy1248873545/article/details/124535761
Author: 礁之
Title: Python(3)高级特性
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/748877/
转载文章受原作者版权保护。转载请注明原作者出处!