Python(3)高级特性(上)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Python(3)高级特性

此文章参考廖雪峰大神的官网,地址:高级特性 - 廖雪峰的官方网站 (liaoxuefeng.com)


一、切片


  • 在python的使用中,对于列表、元组的元素取值是非常常见的,例如:
  • 注意:切片是顾头不顾尾的


>>> L = ["aaa","bbb","ccc","ddd"] 
>>> print(L) 
['aaa', 'bbb', 'ccc', 'ddd']
#如果想取前三个元素,可以这样:
>>> print(L[0],L[1],L[2])   
aaa bbb ccc
#也可以使用for循环:
>>> for i in range(3):
...     a.append(L[i])
... 
>>> print(a) 
['aaa', 'bbb', 'ccc']
#但是明显前两种方法都比较繁琐,所以,python提供了切片(Slice)操作符,这样在取指定元素时可以变得更加简单,例如:
>>> print(L[0:3])   #表示从第0个元素取到第3个元素,由于切片是顾头不顾尾的,所以是不包含第3个元素的,只取出了前三个元素
['aaa', 'bbb', 'ccc']
>>> print(L[:3])    #默认:前面从0开始
['aaa', 'bbb', 'ccc']
#有了切片这个方法后,就可以轻松取出一段元素,例如:
>>> L = list(range(100))   #python3需要把range转换成列表,python2可以直接使用range赋值,range(100)表示0-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[:])     #切片没有指定条件的话,会输出全部
[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])   #取前10
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> print(L[10:20])  #取10-19
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> print(L[:-1])   #使用负数时,是从末尾开始算的,":-1"表示从0到末尾-1,所以是0-98
[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])  #取倒数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]
#如果想每个5个取一个值的话,可以这样做:
>>> print(L[::5])    #表示每隔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函数,利用切片来去除首位的空格:
# -*- coding: utf-8 -*-
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
#但是直接这样循环的话,可以看到,只能取到字典的key,取不到value,取value的话可以这样:
>>> for i in L.values():   #利用values函数来取到value的值
...     print(i)
... 
1
2
3
#想要同时取key和value的话可以使用items函数:
>>> 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    #导入collections.abc模块的Iterable方法
>>> isinstance('aaaa',Iterable) 
True  #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):  #使用enumerate函数可以输出“下标 + 元素”的格式
...     print(x,y) 
... 
0 1
1 2
2 3
3 4
4 5
#利用for循环来达到这种效果
>>> L = [(1,2),(3,4),(5,6)]  
>>> for x,y in L:
...     print(x,y) 
... 
1 2
3 4
5 6
#取列表中的最大值和最小值:
# -*- coding: utf-8 -*-
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]
#如果要使用列表生成[1*1,2*2,3*3,...,10*10]这样的可以使用for循环
>>> 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循环来创建列表推导式,例如:


#只想输出1-10的偶数
>>> 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]
这个推导式的意思是除以二余零的直接输出,否则变成负数输出
#还可以使用多层for循环来进行交叉输出
>>> [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 "<stdin>", line 1, in <module>
  File "<stdin>", 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 "<stdin>", line 1, in <module>
  File "<stdin>", 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>
可以发现生成器不会直接输出结果
#输出生成器的结果,使用next()
>>> 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 "<stdin>", line 1, in <module>
StopIteration
#可以看到每次使用next就会输出循环后续的结果,直至循环完成,但是这样一个一个敲太麻烦了,而生成器也是可迭代的数据,所以我们可以使用for循环
>>> 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去依次调用
  • 下面引用一个案例,实现斐波拉契数列


# -*- coding: utf-8 -*-
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就可以变成一个生成器:
# -*- coding: utf-8 -*-
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语句然后再次进行返回,这里的函数其实就变成了生成器函数
#如果这里的解析比较难理解的话,可以看一下这个简单的示例:
# -*- coding: utf-8 -*-
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月前
|
存储 大数据 数据处理
Python 中的列表推导式与生成器:特性、用途与区别
Python 中的列表推导式与生成器:特性、用途与区别
21 2
|
2月前
|
机器学习/深度学习 人工智能 安全
python和Java的区别以及特性
Python:适合快速开发、易于维护、学习成本低、灵活高效。如果你需要快速上手,写脚本、数据处理、做点机器学习,Python就是你的首选。 Java:适合大型项目、企业级应用,性能要求较高的场景。它类型安全、跨平台能力强,而且有丰富的生态,适合更复杂和规模化的开发。
47 3
|
1月前
|
设计模式 监控 安全
Python多线程编程:特性、挑战与最佳实践
Python多线程编程:特性、挑战与最佳实践
35 0
|
1月前
|
设计模式 监控 安全
Python多线程编程:特性、挑战与最佳实践【1】
Python多线程编程:特性、挑战与最佳实践【1】
32 0
|
3月前
|
存储 数据库 C++
"深入剖析Python元组(tuple):与列表的对比、特性解析及高效应用场景展示"
【8月更文挑战第9天】Python元组与列表虽均用于存储元素集合,但有本质差异。元组不可变,创建后无法修改,适合保护数据不被意外更改的场景,如作字典键或传递固定值。列表则可变,支持动态增删改,适用于需频繁调整的数据集。元组因不可变性而在性能上有优势,可用于快速查找。两者各有千秋,根据具体需求选择使用。例如,元组可用于表示坐标点或日期,而列表更适合管理用户列表或库存。
102 1
|
3月前
|
安全 算法 Go
Python面向对象的三大特性
python面向对象编程(OOP)的三大特性是封装、继承和多态。这些特性共同构成了OOP的基础,使得软件设计更加灵活、可维护和可扩展。
29 3
|
3月前
|
机器学习/深度学习 运维 数据挖掘
scikit-learn 1.0 版本重要新特性一览
scikit-learn 1.0 版本重要新特性一览
|
4月前
|
机器学习/深度学习 数据采集 前端开发
网络爬虫开发:JavaScript与Python特性的小差异
我们以前写JavaScript的代码时,在遇到了发送请求时,都是需要去await的。 但是为什么Python代码不需要这样做呢? 这就是因为JavaScript是异步的,Python是同步的。 JavaScript就需要使用关键词await将异步代码块变为同步代码。
|
4月前
|
数据库 开发者 Python
Python 3.9的新特性有哪些?
【7月更文挑战第2天】Python 3.9的新特性有哪些?
53 1
|
5月前
|
API 项目管理 开发者
PEP是Python改进的关键文档,用于提议新特性和标准化变更
【6月更文挑战第26天】PEP是Python改进的关键文档,用于提议新特性和标准化变更。它们提出功能设计,记录社区决策,建立标准,促进共识,并改进开发流程。PEP是Python不断演进和优化的核心机制,驱动语言的未来发展。**
47 2