Python官方中文教程(转) 3

8 篇文章 4 订阅
5 篇文章 0 订阅

数据结构


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()函数,当不存在 genderkey时,默认返回 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 中已经取消了,原因是有一种更简单直观的方法,那就是使用 innot 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


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值