Python中文指南 3
转自 https://python.iswbm.com/preface.html
1.列表 [ ]
列表(英文名 list),是由一系列元素按顺序进行排列而成的容器。
这里面有两个重点:
1.元素:没有要求同一类型,所以可以是任意类型。
2.顺序:按顺序排列而成,说明列表是有序的。
在接下来的例子中,我会向你演示,列表的一些特性和常用的方法。
1.1创建列表
创建列表有两种方法
第一种方法:
先创建空列表实例 list ( )
再往实例中添加元素
>>> phones = list() # 实例化
>>> phones.append("Apple") # 添加元素
>>> phones.append("Huawei") # 添加元素
>>> phones.append("Xiaomi") # 添加元素
>>> phones
['Apple', 'Huawei', 'Xiaomi']
第二种方法:
直接定义列表 list = [ 1, 2 ]
并填充元素。
>>> phones = ["Apple", "Huawei", "Xiaomi"]
>>> phones
['Apple', 'Huawei', 'Xiaomi']
很明显,第二种最简单直接,容易理解。并且经过测试,第二种的效率也比第一种的要高。因此推荐新手使用第二种。
1.2 增删改查
增删改查:是 新增元素、删除元素、修改元素、查看元素的简写。
由于,内容比较简单,让我们直接看演示
查看元素
使用[i]
的方式查看第i+1
个元素。例如 x
的起始值为 0
,代表第一个元素。
>>> phones = ["Apple", "Huawei", "Xiaomi"]
>>> phones[0]
'Apple'
>>> phones[1]
'Huawei'
>>> phones[2]
'Xiaomi'
使用 index()
在这里插入图片描述
方法,查看第一个值为 x
的索引。
>>> phones = ["Apple", "Huawei", "Xiaomi", "Huawei"]
>>> phones.index("Huawei")
1
使用 count()
方法,查看该列表中有几个值为x
>>> phones = ["Apple", "Huawei", "Xiaomi", "Huawei"]
>>> phones.count("Huawei")
2
使用内置函数 len()
,可以查看该列表中有几个值
>>> phones = ["Apple", "Huawei", "Xiaomi"]
>>> len(phones)
3
新增元素
使用列表的append()
、insert()
、和 extend()
方法
append ()
方法:将元素插入在列表的最后一个位置
>>> phones = []
>>> phones
[]
>>> phones.append("Apple")
>>> phones
['Apple']
>>> phones.append("Huawei") # append 后 Huawei 会在最后一个位置
>>> phones
['Apple', 'Huawei']
insert()
方法:将元素插入在列表的指定的位置
>>> phones = ["Apple", "Huawei", "Xiaomi"]
>>> phones.insert(1, "OPPO") # 把 OPPO 插入到索引为 1 的位置
>>> phones
['Apple', 'OPPO', 'Huawei', 'Xiaomi']
extend()
:将一个新的列表直接连接在旧的列表后面
>>> phones = ["Apple", "Huawei", "Xiaomi"]
>>> new_phones = ["OPPO", "VIVO"]
>>> phones.extend(new_phones)
>>> phones
['Apple', 'Huawei', 'Xiaomi', 'OPPO', 'VIVO']
修改元素
直接使用list[x]=new_item
的方法直接替换
>>> phones = ["Apple", "Huawei", "Xiaomi"]
>>> phones[1] = "OPPO"
>>> phones
['Apple', 'OPPO', 'Xiaomi']
删除元素
使用 pop()
,remove()
、clear()
方法或者 del()
语句删除元素
pop()
方法:删除指定位置的元素。默认删除最后一个元素,并返回
>>> phones = ["Apple", "Huawei", "Xiaomi"]
>>> phones.pop() # 删除最后一个元素
'Xiaomi'
>>> phones.pop(0) # 删除索引为0的元素
'Apple'
>>> phones
['Huawei']
remove()
:删除第一个值为 x 的元素。
>>> phones = ["Apple", "Huawei", "Xiaomi", "Huawei"]
>>> phones.remove("Huawei")
>>> phones
['Apple', 'Xiaomi', 'Huawei']
clear ()
方法:把所有的元素清空
>>> phones = ["Apple", "Huawei", "Xiaomi"]
>>> phones.clear()
>>> phones
[]
del()
语句:清空列表,还有另一种方法
>>> phones = ["Apple", "Huawei", "Xiaomi"]
>>> del phones[:]
>>> phones
[]
使用 del ()
语句,还可以删除某一个或者某几个连续的元素。
>>> phones = ["Apple", "Huawei", "Xiaomi", "OPPO", "VIVO"]
>>> del phones[0] # 删除索引为0的元素
>>> phones
['Huawei', 'Xiaomi', 'OPPO', 'VIVO']
>>>
>>> del phones[1:3] # 删除索引在 [1:3) 区间内元素,注意是左闭右开区间
>>> phones
['Huawei', 'VIVO']
1.3 列表反转
列表反转有两种方法
第一种方法:使用
自带的 reverse ( )
>>> nums = [1,2,3,4,5]
>>> nums.reverse()
>>> nums
[5, 4, 3, 2, 1]
第二种方法:
切片的方法
>>> nums = [1,2,3,4,5]
>>> nums[::-1]
[5, 4, 3, 2, 1]
这两种方法,区别在于:
reverse()
方法是原地反转,作用在原对象上
切片反转是返回一个新对象,原对象不改变
1.4 列表排序
列表的排序同样有两种方法:
第一种方法:列表对象
内置 sort ( )
可方便我们对元素进行排序。
>>> alist = [4,8,1,7,2]
>>> alist.sort()
>>> alist
[1, 2, 4, 7, 8]
第二种方法:Python 有个
内置的 sorted ( )
它不仅可用作列表的排序,后面我们还会学到 字典 等其他数据结构的排序也会用到它。
>>> alist = [4,8,1,7,2]
>>> sorted(alist)
[1, 2, 4, 7, 8]
不管用哪种方法,都要保证列表内的元素俩俩是可比较的。
比如,数值和数值是可比较的,字符串和字符串之间是可比较的。
但是数值和字符串是不可比较的,示例如下
>>> alist = [9,3,1,"d","k","a"]
>>> alist.sort()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'str' and 'int'
除了上面介绍的俩种之外,其实利用 sort()
还可以实现自定义排序,这部分内容对于新手来说学习起来稍有点难度,且用到的场景也不多,因此这边我就不介绍啦。
元组 ( )
元组(英文名 tuple),和列表非常的相似,它也是由一系列元素按顺序进行排列而成的容器。
不同的是,元组是不可变的,而列表是可变的。
2.1 创建元组
创建元组有三种方法
第一种方法:
直接使用 ( ) 将所有的元素进行包围
这有别于创建列表时使用的是中括号:[]
>>> atuple = (1,2,3,4)
>>> atuple
(1, 2, 3, 4)
>>>
第二种方法:有时候,创建元组时,
圆括号可有可无的
>>> btuple = 1,2,3,4
>>> btuple
(1, 2, 3, 4)
>>>
第三种方法:
使用元组推导式生成一个生成器
由于元组是不可变的,所以生成一个生成器对象。这一种对于新手来说可能会比较难以理解,我会放在后面专门进行讲解,这里先作了解,新手可直接跳过。
>>> ctuple = (i for i in range(1,6))
>>> ctuple
<generator object <genexpr> at 0x10a288f90>
上面三种方法介绍完毕~
你以为就这么简单?
当你在创建只有一个元素的元组时,你有可能会这样子创建
>>> ctuple = (1)
>>> type(ctuple)
<class 'int'>
>>> ctuple
1
>>>
却发现,创建出来的并不是tuple
,而是一个int
对象。
此时千万要记住,当你
创建只包含一个元素的元组时,要在第一个元素后面加一个逗号
>>> ctuple = (1,)
>>> type(ctuple)
<class 'tuple'>
>>> ctuple
(1,)
>>>
>>> dtuple = 1,
>>> type(dtuple)
<class 'tuple'>
>>> dtuple
(1,)
空元组 a = tuple()
另外,创建空元组可以这样
>>> a = tuple() # 第一种方法
>>> a
()
>>> type(a)
<class 'tuple'>
>>> b = () # 第二种方法
>>> b
()
>>> type(b)
<class 'tuple'>
2.2 增删改查
最前面我们说过,
元组是不可变的,对元组进行修改的行为都是不被允许的。
查看元素
查看元素可以,但是修改元素和删除元素都报错了。
>>> atuple = (1,2,3,4)
>>> atuple[0] # 查看元素
1
>>> atuple[0] = 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>>
>>> del atuple[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object doesn't support item deletion
新增元素呢?当然同样也是不支持的,这里不再演示。
2.3 元组与列表的转换
虽然元组可能看起来与列表很像,但它们通常是在不同的场景被使用,并且有着不同的用途。
元组是 immutable
(不可变的),其序列通常包含不同种类的元素,并且通过解包或者索引来访问(如果是 namedtuples
的话甚至还可以通过属性访问)。
列表是 mutable
(可变的),并且列表中的元素一般是同种类型的,并且通过迭代访问。
那有办法可以实现二者的转换吗?
当然有,而且非常简单。
元组转成列表
>>> atuple = (1,2,3,4)
>>> type(atuple)
<class 'tuple'>
>>>
>>>
>>> list(atuple)
[1, 2, 3, 4]
>>>
列表转成元组
>>> alist = [1,2,3,4]
>>> type(alist)
<class 'list'>
>>>
>>>
>>> tuple(alist)
(1, 2, 3, 4)
字典 { }
字典(英文名 dict),它是由一系列的键值(key-value)
对组合而成的数据结构。
字典中的每个键都与一个值相关联,其中
1.键,必须是可 hash 的值,如字符串,数值等
2.值,则可以是任意对象
3.1 创建字典
创建一个字典有三种方法
第一种方法:先使用
dict() 创建空字典实例,再往实例中添加元素
>>> profile = dict(name="王炳明", age=27, 众号="ython编程时光")
>>> profile
{'name': '王炳明', 'age': 27, '众号': 'ython编程时光'}
第二种方法:直接
使用 { } 定义字典,并填充元素。
>>> profile = {"name": "王炳明", "age": 27, "公众号": "Python编程时光"}
>>> profile
{'name': '王炳明', 'age': 27, '公众号': 'Python编程时光'}
第三种方法:使用
dict() 构造函数从键值对序列里创建字典。
>>> info = [('name', '王炳明 '), ('age', 27), ('公众号', 'Python编程时光')]
>>> dict(info)
{'name': '王炳明 ', 'age': 27, '公众号': 'Python编程时光'}
第四种方法:使用
字典推导式
这一种对于新手来说可能会比较难以理解,我会放在后面专门进行讲解,这里先作了解,新手可直接跳过。
>>> adict = {x: x**2 for x in (2, 4, 6)}
>>> adict
{2: 4, 4: 16, 6: 36}
3.2 增删改查
增删改查:是 新增元素、删除元素、修改元素、查看元素的简写。
由于,内容比较简单,让我们直接看演示
查看元素 dict[key]
查看或者访问元素,直接使用 dict[ key ]
的方式就可以
>>> profile = {"name": "王炳明", "age": 27, "公众号": "Python编程时光"}
>>> profile["公众号"]
'Python编程时光'
但这种方法,在 key
不存在时会报 KeyValue
的异常
>>> profile = {"name": "王炳明", "age": 27, "公众号": "Python编程时光"}
>>> profile["gender"]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'gender'
所以更好的查看获取值的方法是使用 get()
函数,当不存在 gender
的key
时,默认返回 male
>>> profile = {"name": "王炳明", "age": 27, "公众号": "Python编程时光"}
>>> profile.get("gender", "male")
'male'
新增元素
新增元素 dict [ key ] = value
>>> profile = dict()
>>> profile
{}
>>> profile["name"] = "王炳明"
>>> profile["age"] = 27
>>> profile["公众号"] = "Python编程时光"
>>> profile
{'name': '王炳明','age': 27,'公众号': 'Python编程时光'}
修改元素
修改元素 dict [ key ] = new_value
>>> profile = {"name": "王炳明", "age": 27, "公众号": "Python编程时光"}
>>>
>>> profile["age"] = 28
>>> profile
{'name': '王炳明', 'age': 28, '公众号': 'Python编程时光'}
删除元素
删除元素
有三种方法
第一种方法:使用
pop ( )
>>> profile = {"name": "王炳明", "age": 27, "公众号": "Python编程时光"}
>>> profile.pop("age")
27
>>> profile
{'name': '王炳明', '公众号': 'Python编程时光'}
第二种方法:使用
del ( )
>>> profile = {"name": "王炳明", "age": 27, "公众号": "Python编程时光"}
>>> del profile["age"]
>>> profile
{'name': '王炳明', '公众号': 'Python编程时光'}
3.4 重要方法
判断key是否存在 has_key( )
在 Python 2 中的字典对象有一个 has_key 函数
,可以用来判断一个key
是否在该字典中
>>> profile = {"name": "王炳明", "age": 27, "公众号": "Python编程时光"}
>>> profile.has_key("name")
True
>>>
>>> profile.has_key("gender")
True
in 和 not in
但是这个方法在 Python 3 中已经取消了,原因是有一种更简单直观的方法,那就是使用 in
和 not in
来判断。
>>> profile = {"name": "王炳明", "age": 27, "公众号": "Python编程时光"}
>>> "name" in profile
True
>>>
>>> "gender" in profile
False
设置默认值
要给某个 key
设置默认值,最简单的方法
profile = {"name": "王炳明", "age": 27, "公众号": "Python编程时光"}
if "gender" not in profile:
profile["gender"] = "male"
实际上有个更简单的方法
setdefault ( key ,val )
profile = {"name": "王炳明", "age": 27, "公众号": "Python编程时光"}
profile.setdefault("gender", "male")
迭代器
4.1 可迭代对象
可以利用 for
循环的对象,都叫可迭代对象。
譬如我们前面学过的 列表、元组、字典、字符串等都是可迭代对象。
# 以列表为例
>>> alist = [0, 1, 2, 3, 4, 5]
>>> for i in alist:
... print(i)
...
0
1
2
3
4
5
是否可迭代?
对 Python 比较熟悉的朋友,肯定知道哪些数据类型是可迭代的,哪些是不可迭代的。
但是对新手来说,可能需要借助一些函数来判别,比如 Python 内置的collections.abc
模块,这个模块只有在 Python 中才有噢,在这个模块中提供了一个Iterable
类,可以用isinstance
来判断。
>>> from collections.abc import Iterable
>>>
>>> isinstance([0, 1, 2], Iterable) # 列表
True
>>> isinstance({"name": "王炳明"}, Iterable) # 字典
True
>>> isinstance((1,2,3), Iterable) # 元组
True
>>> isinstance("hello", Iterable) # 字符串
True
但是这种方法并不是百分百准确(具体下面会说到),最准确的方法,还是应该使用for
循环。
4.2 可迭代协议
可迭代对象内部是如何实现在你对其进行 for
循环时,可以一个一个元素的返回出来呢?
这就要谈到迭代器协议。
第一种场景:如果一个对象内部实现了__iter__()
方法 ,并返回一个迭代器实例,那么该对象就是可迭代对象
class Array:
mylist = [0,1,2]
# 返回迭代器类的实例
def __iter__(self):
return iter(self.mylist)
# 得到可迭代对象
my_list = Array()
print(isinstance(my_list, Iterable)) # True
for i in my_list:
print(i)
第二种场景:假设一个对象没有实现__iter__()
,Python 解释器 __getitem__()
方法获取元素,如果可行,那么该对象也是一个可迭代对象。
from collections.abc import Iterable
class Array:
mylist = [0,1,2]
def __getitem__(self, item):
return self.mylist[item]
# 得到一个可迭代对象
my_list = Array()
print(isinstance(my_list, Iterable)) # False
for i in my_list:
print(i)
此时如果你使用 isinstance( my_list, Iterable)
去判断是否是可迭代,就会返回 False
,因为 isinstance
这种方法就是检查对象是否有__iter__
方法。这也论证了使用isinstance( my_list, Iterable)
去判断是否可迭代是不准确的。
4.3 什么是迭代器
使用 iter()
函数对一个可迭代对象处理后,它会返回一个迭代器对象,对于迭代器对象,我们可以使用next
函数,去获取元素,每执行一次,获取一次,等到全部获取完毕,会抛出 StopIteration
提示无元素可取。
创建迭代器 iter ( 可迭代对象 )
>>> alist = [0, 1, 2, 3]
>>> gen = iter(alist)
>>> gen
<list_iterator object at 0x100a94b20>
>>> next(gen)
0
>>> next(gen)
1
>>> next(gen)
2
>>> next(gen)
3
>>> next(gen)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
4.4 迭代器协议
对比可迭代对象,迭代器的内部只是多了一个函数而已 __next__()
正因为有了它,我们才可以用 next()
来获取元素。
迭代器,是在可迭代的基础上实现的。要创建一个迭代器,我们首先,得有一个可迭代对象。 现在就来看看,如何创建一个可迭代对象,并以可迭代对象为基础创建一个迭代器。
from collections.abc import Iterator
class Array:
index = 0
mylist = [0,1,2]
# 返回该对象的迭代器类的实例
# 因为自己就是迭代器,所以返回self
def __iter__(self):
return self
# 当无元素时,必要抛出 StopIteration
def __next__(self):
if self.index <= len(self.mylist)-1:
value = self.mylist[self.index]
self.index += 1
return value
raise StopIteration
my_iterator = iter(Array())
print(isinstance(my_iterator, Iterator)) # output: True
print(next(my_iterator)) # output: 0
print(next(my_iterator)) # output: 1
print(next(my_iterator)) # output: 2
print(next(my_iterator)) # StopIteration
生成器
5.1 什么是生成器
生成器(英文名 Generator ),是一个可以像迭代器那样使用for循环来获取元素的函数。
生成器的出现(Python 2.2 +),实现了延时计算,从而缓解了在大量数据下内存消耗过猛的问题。
当你在 Python Shell
中敲入一个生成器对象,会直接输出 generator object
提示你这是一个生成器对象
>>> gen = (i for i in range(5))
>>> gen
<generator object <genexpr> at 0x10cae50b0>
5.2 如何创建生成器?
使用列表推导式
在上面已经演示过,正常我们使用列表推导式时是下面这样子,使用[]
,此时生成的是列表。
>>> mylist = [i for i in range(5)]
>>> mylist
[0, 1, 2, 3, 4]
而当你把[]
换成 ()
,返回的就不是列表了,而是一个生成器
>>> gen = (i for i in range(5))
>>> gen
<generator object <genexpr> at 0x10cae50b0>
使用 yield
yield
是什么东西呢? 它相当于我们函数里的 return
,但与 return
又有所不同。
当一个函数运行到 yield
后,函数的运行会暂停,并且会把 yield
后的值返回出去。
若 yield
没有接任何值,则返回 None
yield
虽然返回了,但是函数并没有结束
请看如下代码,我定义了一个 generator_factory()
函数,当我执行gen = generator_factory()
时,gen
就是一个生成器对象
>>> def generator_factory(top=5):
... index = 0
... while index < top:
... print("index 值为: " + str(index))
... index = index + 1
... yield index
... raise StopIteration
...
>>> gen = generator_factory()
>>> gen
<generator object generator_factory at 0x1018340b0>
5.3 生成器的使用
从一个生成器对象中取出元素,和我们前面学过的通过切片访问列表中的元素不一样,它没有那么直观。
想要从生成器对象中取出元素,只有两种方法:
第一种方法:
使用 next 方法一个一个地把元素取出来
如果元素全部取完了,生成器会抛出 StopIteration
的异常。
>>> gen = (x for x in range(3))
>>> gen
<generator object <genexpr> at 0x1072400b0>
>>> next(gen)
0
>>> next(gen)
1
>>> next(gen)
2
>>> next(gen)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
第二种方法:
使用 for 循环一个一个地迭代出来
>>> gen = (x for x in range(3))
>>> for i in gen:
... print(i)
...
0
1
2
5.4 生成器的激活
生成器对象,在创建后,并不会执行任何的代码逻辑。
想要从生成器对象中获取元素,那么第一步要触发其运行,在这里称之为激活。
方法有两种:
使用next() :
上面已经讲过
使用generator.send(None)
还以下面这段代码为例,可以看到 gen.send(None)
相当于执行了 next(gen)
>>> def generator_factory(top=5):
... index = 0
... while index < top:
... print("index 值为: " + str(index))
... index = index + 1
... yield index
... raise StopIteration
...
>>>
>>> gen = generator_factory()
>>> gen.send(None)
index 值为: 0
1
>>> gen.send(None)
index 值为: 1
2
生成器的状态
生成器在其生命周期中,会有如下四个状态
1.GEN_CREATED # 生成器已创建,还未被激活
2.GEN_RUNNING # 解释器正在执行(只有在多线程应用中才能看到这个状态)
3.GEN_SUSPENDED # 在 yield 表达式处暂停
4.GEN_CLOSED # 生成器执行结束
通过下面的示例可以很轻松地理解这一过程(GEN_RUNNING
这个状态只有在多线程中才能观察到,这里就不演示啦)
>>> gen = (x for x in range(2))
>>> from inspect import getgeneratorstate
>>> gen = (x for x in range(3))
>>> getgeneratorstate(gen)
'GEN_CREATED'
>>>
>>> next(gen)
0
>>> getgeneratorstate(gen)
'GEN_SUSPENDED'
>>> next(gen)
1
>>> next(gen)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> getgeneratorstate(gen)
'GEN_CLOSED'
5.5 生成器的异常
在最前面,我有定义了一个生成器函数。
def generator_factory(top=2):
index = 0
while index < top:
index = index + 1
yield index
raise StopIteration
在没有元素可返回时,我最后抛出了StopIteration
异常,这是为了满足生成器的协议。
实际上,如果你不手动抛出 StopIteration
,在生成器遇到函数 return
时,会我自动抛出 StopIteration
。
请看下面代码,我将 raise StopIteration
去掉后,仍然会抛出异常。
>>> def generator_factory(top=2):
... index = 0
... while index < top:
... index = index + 1
... yield index
...
>>> gen = generator_factory()
>>> next(gen)
1
>>> next(gen)
2
>>> next(gen)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration