转 Python学习(八)
八、数据结构
数据结构就是可以将一些数据结合到一起的数据结构,换言之用于存储一组相关的数据,Python有4种内建数据结构:列表,元组,字典和集合。
1、列表
列表是一种用于保存有序元素集合的数据结构,即你可以在列表中存储元素序列。考虑一个购物清单,上面有你需要购买的物品列表,只不过你可能希望以分号分隔他们而到Python变成了逗号。
列表元素被包含在方括号中,这样Python就会才会明白你指定的是一个列表。一点列表创建完毕,我们可以对其元素进行添加,删除和搜索。正是因为可以执行添加和删除操作,我们将列表称作可变类型,即这种类型可以被修改。
对象和类的快速简介
列表是使用对象和类的一个例子。当我们对变量i赋值时,例如赋值5,这相当于创建一个Int类(类型)的对象(实例),你可以参看help(int)来更好的理解它。
一个类同样可以拥有方法,即函数,而他们只能应用与这个类。并且只有当你拥有一个类的对象时才能使用这些功能。例如,Python为列表提供了一个append方法允许我们将元素添加到列表的表尾。如,mylist.append(‘an item’)将字符串添加到列表mylist的尾部。注意,要用点号访问对象方法。
一个类还可以拥有字段,而字段只不过是专门应用与一个类的变量而已。当你拥有对应类的对象时就能使用这些变量了。字段同样利用点号访问,如,mylist.field。
例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
# Filename: using_list.py # This is my shopping list shoplist = [ 'apple' , 'mango' , 'carrot' , 'banana' ] print ( 'I have' , len (shoplist), 'items to purchase.' ) print ( 'These items are:' , end = ' ' ) for item in shoplist: print (item, end = ' ' ) print ( '\nI also have to buy rice.' ) shoplist.append( 'rice' ) print ( 'My shopping list is now' , shoplist) print ( 'I will sort my list now' ) shoplist.sort() print ( 'Sorted shopping list is' , shoplist) print ( 'The first item I will buy is' , shoplist[ 0 ]) olditem = shoplist[ 0 ] del shoplist[ 0 ] print ( 'I bought the' , olditem) print ( 'My shopping list is now' , shoplist) |
输出:
C:\Users\Administrator>python D:\python\using_list.py
I have 4 items to purchase.
These items are: apple mango carrot banana
I also have to buy rice.
My shopping list is now ['apple', 'mango', 'carrot', 'banana', 'rice']
I will sort my list now
Sorted shopping list is ['apple', 'banana', 'carrot', 'mango', 'rice']
The first item I will buy is apple
I bought the apple
My shopping list is now ['banana', 'carrot', 'mango', 'rice']
工作原理:
变量shoplist是某个购物人的购物清单。我们只在shoplist中存储被购买物品的名字字符串,但你也可以为列表增加任何其它种类对象,包括数字甚至是其它列表。我们通过for…in迭代列表元素,现在我们可以看到列表其实也是一个序列。
注意print的end关键字实参,他指定我们希望以空格结束输出而不是通常的换行。
接下来我们使用列表对象的append方法为列表添加一个新的元素。 为了确定元素真的被添加进去了,我们简单的将列表传给print函数,print函数整洁的将列表内容打印出来。 随后我们使用列表的sort方法对列表进行排序,注意sort会影响列表本身而不是返回一个被修改后的列表。这与字符串的工作方式不同,这也是为什么说类标是可变类型而字符串是不可变类型的原因。 然后当在市场购买一样东西后,我们希望将其从列表中删除,del语句用处在此。在这里我们指出希望删除列表中的哪个元素,de就将这个元素从列表中删除。我们指定的是希望删除列表的第一个元素,因此我们使用del shoplist[0](回想一下,python的索引从0开始)。
如果你想知道list对象的所有方法,详见help(list)。
2、元组
元组用于保存各种各样的对象。它与列表很相似,但它缺少列表提供的大量功能。列表的一个主要特点就象字符串一样,它是不可变类型,也就是说你不可以修改元组。
元组通过一组以逗号分隔的元素定义,并以一个可选的小括号闭合。
元组通常用于这样的情形,一个语句或一个用户定义的函数能够安全的假设其使用的一组值(即元组值)不会发生改变。
例如:
1
2
3
4
5
6
7
8
9
10
11
|
# Filename: using_tuple.py zoo = ( 'python' , 'elephant' , 'penguin' ) # remember the parentheses are optional print ( 'Number of animals in the zoo is' , len (zoo)) new_zoo = 'monkey' , 'camel' , zoo print ( 'Number of cages in the new zoo is' , len (new_zoo)) print ( 'All animals in new zoo are' , new_zoo) print ( 'Animals brought from old zoo are' , new_zoo[ 2 ]) print ( 'Last animal brought from old zoo is' , new_zoo[ 2 ][ 2 ]) print ( 'Number of animals in the new zoo is' , len (new_zoo) - 1 + len (new_zoo[ 2 ])) |
输出:
C:\Users\Administrator>python D:\python\using_tuple.py
Number of animals in the zoo is 3
Number of cages in the new zoo is 3
All animals in new zoo are ('monkey', 'camel', ('python', 'elephant', 'penguin')
)
Animals brought from old zoo are ('python', 'elephant', 'penguin')
Last animal brought from old zoo is penguin
Number of animals in the new zoo is 5
工作原理:
变量zoo引用一个元组。我们看到len函数可以得到元组的长度。这也表明元组同样是一个序列类型。 接着我们将这些动物转移到一个新的动物园。因此元组new_zoo既包含已有的动物又包含从老动物园转移过来的新动物。
注意一个包含在其它元组内的元组并不会丢失它的身份。
像列表一样,我们可以通过一对方括号指定元素的位置访问这个元素。这叫做索引操作符。我们通过new_zoo[2]访问new_zoo的第三个元素,通过new_zoo[2][2]访问new_zoo的第三个元素的第三个元素。一但你理解这种语言风格,你会觉得这样的操作太简单了。
小括号
虽然小括号是可选的,但强烈建议你坚持使用小括号,这样一眼就能看出它是个元组,尤其还能避免出现歧义。
例如,print(1, 2, 3)和print((1, 2, 3))是不同的 – 前者打印3个数字而后者打印一个元组(包含3个数字)。
拥有0个或一个元素的元组
一个空元组通过空圆括号创建,例如myempty = ()。
不过,指定一个单元素元组就不那么直观了。你必须在其仅有的一个元素后跟随一个逗号,这样python才能区分出
你要的是元组而不是一个被小括号包围的对象的表达式。例如你想要一个包含值为2的单元素元组,则必须写成singleton = (2, )
注意:列表之中的列表不会失去它的身份,即列表不会像Perl中那样被打散。同样元组中的元组,或列表中的元组,或元组中的列表等等都是如此。只要是Python,它们就只是使用另一个对象存储的对象。
3、字典
字典就像通讯录,只要知道联系人的名字就能找到他的地址或详细信息。即我们将键(名字)与值(相关信息)联系到一起。
注意:
键必须是唯一的,这就像如果两个人同名你就没法找到正确的信息了。
字典的键必须是不可变对象(比如字符串),但字典的值可以是可变或不可变对象。基本上这意味着只能将简单的对象作为键。
字典中的键值对使用语法d = {key1 :value1, key2: value2}指定。
注意它们的键/值对用冒号分割,而各个对用逗号分割,所有这些都包括在花括号中。
记住字典中的键/值对是无序的。如果你希望按照特定的顺序排列它们,你只能在使用前自己排序。
而你实际使用的字典是dict类的对象/实例。
例如:
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
|
# Filename: using_dict.py # 'ab' is short for 'a'ddress'b'ook ab = { 'Swaroop' : 'swaroop@swaroopch.com' , 'Larry' : 'larry@wall.org' , 'Matsumoto' : 'matz@ruby-lang.org' , 'Spammer' : 'spammer@hotmail.com' } print ( "Swaroop's address is" , ab['Swaroop']) # Deleting a key-value pair del ab[ 'Spammer' ] print ( '\nThere are {0} contacts in the address-book\n' . format ( len (ab))) for name, address in ab.items(): print ( 'Contact {0} at {1}' . format (name, address)) # Adding a key-value pair ab[ 'Guido' ] = 'guido@python.org' if 'Guido' in ab: print ( "\nGuido's address is" , ab['Guido']) |
输出:
C:\Users\Administrator>python D:\python\using_dict.py
Swaroop's address is swaroop@swaroopch.com
There are 3 contacts in the address-book
Contact Swaroop at swaroop@swaroopch.com
Contact Matsumoto at matz@ruby-lang.org
Contact Larry at larry@wall.org Guido's address is guido@python.org
工作原理:
我们使用已经介绍过的标记创建了字典ab。然后我们使用在列表和元组章节中已经讨论过的索引操作符来指定键,从而使用键/值对。我们可以看到字典的语法同样十分简单。
我们可以使用我们的老朋友——del
语句来删除键/值对。我们只需要指明字典和用索引操作符指明要删除的键,然后把它们传递给del
语句就可以了。执行这个操作的时候,我们无需知道那个键所对应的值。
接下来,我们使用字典的items
方法,来使用字典中的每个键/值对。这会返回一个元组的列表,其中每个元组都包含一对项目——键与对应的值。我们抓取这个对,然后分别赋给for..in
循环中的变量name
和address
然后在for-块中打印这些值。
我们可以使用索引操作符来寻址一个键并为它赋值,这样就增加了一个新的键/值对,就像在上面的例子中我们对Guido所做的一样。
我们可以使用in
操作符来检验一个键/值对是否存在,或者使用dict
类的has_key
方法。你可以使用help(dict)
来查看dict
类的完整方法列表。
关键字参数与字典
如果换一个角度看待你在函数中使用的关键字参数的话,你已经使用了字典了!只需想一下——你在函数定义的参数列表中使用的键/值对。当你在函数中使用变量的时候,它只不过是使用一个字典的键(这在编译器设计的术语中被称作符号表 )。
4、序列
列表、元组和字符串都是序列,但是序列是什么,它们为什么如此特别呢?序列的两个主要特点是索引操作符和切片操作符。索引操作符让我们可以从序列中抓取一个特定项目。以上说到的三种序列类型 – lists,tuples,strings还支持一种切片操作,允许我们得到序列的一个切片,即序列的部分。
例如:
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
|
# Filename: seq.py shoplist = [ 'apple' , 'mango' , 'carrot' , 'banana' ] name = 'swaroop' # Indexing or 'Subscription' operation print ( 'Item 0 is' , shoplist[ 0 ]) print ( 'Item 1 is' , shoplist[ 1 ]) print ( 'Item 2 is' , shoplist[ 2 ]) print ( 'Item 3 is' , shoplist[ 3 ]) print ( 'Item -1 is' , shoplist[ - 1 ]) print ( 'Item -2 is' , shoplist[ - 2 ]) print ( 'Character 0 is' , name[ 0 ]) # Slicing on a list print ( 'Item 1 to 3 is' , shoplist[ 1 : 3 ]) print ( 'Item 2 to end is' , shoplist[ 2 :]) print ( 'Item 1 to -1 is' , shoplist[ 1 : - 1 ]) print ( 'Item start to end is' , shoplist[:]) # Slicing on a string print ( 'characters 1 to 3 is' , name[ 1 : 3 ]) print ( 'characters 2 to end is' , name[ 2 :]) print ( 'characters 1 to -1 is' , name[ 1 : - 1 ]) print ( 'characters start to end is' , name[:]) |
输出:
C:\Users\Administrator>python D:\python\seq.py
Item 0 is apple
Item 1 is mango
Item 2 is carrot
Item 3 is banana
Item -1 is banana
Item -2 is carrot
Character 0 is s
Item 1 to 3 is ['mango', 'carrot']
Item 2 to end is ['carrot', 'banana']
Item 1 to -1 is ['mango', 'carrot']
Item start to end is ['apple', 'mango', 'carrot', 'banana']
characters 1 to 3 is wa
characters 2 to end is aroop
characters 1 to -1 is waroo
characters start to end is swaroop
工作原理:
首先,我们来学习如何使用索引来取得序列中的单个项目。这也被称作是下标操作。每当你用方括号中的一个数来指定一个序列的时候,Python会为你抓取序列中对应位置的项目。记住,Python从0开始计数。因此,shoplist[0]抓取第一个项目,shoplist[3]抓取shoplist序列中的第四个元素。
索引同样可以是负数,在那样的情况下,位置是从序列尾开始计算的。因此,shoplist[-1]表示序列的最后一个元素而shoplist[-2]抓取序列的倒数第二个项目。
切片操作符是序列名后跟一个方括号,方括号中有一对可选的数字,并用冒号分割。注意这与你使用的索引操作符十分相似。记住数是可选的,而冒号是必须的。
切片操作符中的第一个数(冒号之前)表示切片开始的位置,第二个数(冒号之后)表示切片到哪里结束。如果不指定第一个数,Python就从序列首开始。如果没有指定第二个数,则Python会停止在序列尾。注意,返回的序列从开始位置 开始 ,刚好在 结束 位置之前结束。即开始位置是包含在序列切片中的,而结束位置被排斥在切片外。
这样,shoplist[1:3]返回从位置1开始,包括位置2,但是停止在位置3的一个序列切片,因此返回一个含有两个项目的切片。类似地,shoplist[:]返回整个序列的拷贝。
你可以用负数做切片。负数用在从序列尾开始计算的位置。例如,shoplist[:-1]会返回除了最后一个项目外包含所有项目的序列切片。
另外你还可以为切片提供第三个实参,它代表步长(默认为1)。
>>> shoplist = ['apple', 'mango', 'carrot', 'banana']
>>> shoplist[::1]
['apple', 'mango', 'carrot', 'banana']
>>> shoplist[::2]
['apple', 'carrot']
>>> shoplist[::3]
['apple', 'banana']
>>> shoplist[::-1]
['banana', 'carrot', 'mango', 'apple']
注意当步长为2时,我们得到索引为0,2…的元素,步长为3时得到0,3…,以此类推。
用python交互解释器(这样你能立即看到结果)尝试切片的各种用法吧。
序列类型最好的地方在于你能够以相同的方式访问元组,列表,字符串。
5、集合
集合是简单对象的无序集合,适合当更关心集合中的元素是否存在而不是它们的顺序或是它们出现的次数的时候。
使用集合,你可以测试从属关系,是否一个集合是另一个集合的子集,或是寻找两个集合的交集等等。
>>> bri = set(['brazil', 'russia', 'india'])
>>> 'india' in bri
True
>>> 'usa' in bri
False
>>> bric = bri.copy()
>>> bric.add('china')
>>> bric.issuperset(bri)
True
>>> bri.remove('russia')
>>> bri & bric # OR bri.intersection(bric)
{'brazil', 'india'}
工作原理:
代码几乎是自说明的,因为它涉及到的基础集合论知识我们已经在学校学过了。
6、引用
当你创建一个对象并将其赋给一个变量的时候,变量只是引用了这个对象,而变量并不代表这个对象本身。换言之,变量名指向你的计算机内存的一部分,而这部分内存用于存储实际的对象。这叫做名字到对象的绑定。
通常你不用关心这些,但你应该知道由于引用造成的一些细微的影响。
例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# Filename: reference.py print ( 'Simple Assignment' ) shoplist = [ 'apple' , 'mango' , 'carrot' , 'banana' ] mylist = shoplist # mylist is just another name pointing to the same object! del shoplist[ 0 ] # I purchased the first item, so I remove it from the list print ( 'shoplist is' , shoplist) print ( 'mylist is' , mylist) # notice that both shoplist and mylist both print the same list without # the 'apple' confirming that they point to the same object print ( 'Copy by making a full slice' ) mylist = shoplist[:] # make a copy by doing a full slice del mylist[ 0 ] # remove first item print ( 'shoplist is' , shoplist) print ( 'mylist is' , mylist) # notice that now the two lists are different |
输出:
C:\Users\Administrator>python D:\python\reference.py
Simple Assignment
shoplist is ['mango', 'carrot', 'banana']
mylist is ['mango', 'carrot', 'banana']
Copy by making a full slice
shoplist is ['mango', 'carrot', 'banana']
mylist is ['carrot', 'banana']
工作原理:
大多数解释已经在程序的注释中了。你需要记住的只是如果你想要复制一个列表或者类似的序列或者其他复杂的对象(不是如整数那样的简单对象),那么你必须使用切片操作符来取得拷贝。如果你只是想要使用另一个变量名,两个名称都 参考 同一个对象,那么如果你不小心的话,可能会引来各种麻烦。
注:记住列表的赋值语句不创建拷贝。你得使用切片操作符来建立序列的拷贝。
7、更多字符串的内容
我们已经在前面详细讨论了字符串。我们还需要知道什么呢?那么,你是否知道字符串也是对象,同样具有方法。这些方法可以完成包括检验一部分字符串和去除空格在内的各种工作。
你在程序中使用的字符串都是str
类的对象。这个类的一些有用的方法会在下面这个例子中说明。如果要了解这些方法的完整列表,请参见help(str)
。
例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# Filename: str_methods.py name = 'Swaroop' # This is a string object if name.startswith( 'Swa' ): print ( 'Yes, the string starts with "Swa"' ) if 'a' in name: print ( 'Yes, it contains the string "a"' ) if name.find( 'war' ) ! = - 1 : print ( 'Yes, it contains the string "war"' ) delimiter = '_*_' mylist = [ 'Brazil' , 'Russia' , 'India' , 'China' ] print (delimiter.join(mylist)) |
输出:
C:\Users\Administrator>python D:\python\str_methods.py
Yes, the string starts with "Swa"
Yes, it contains the string "a"
Yes, it contains the string "war"
Brazil_*_Russia_*_India_*_China
工作原理:
在这里我们看到了许多字符串方法的用法。
startswith方法用于确定字符串是否以指定的字符串开头。而in操作检查一个字符串是否是另一个字符串的一部分。
find方法用来寻找给定的字符串在字符串中的位置,如果没找到对应的子串则返回-1。
str类还有一个简洁的连接序列中每个字符串并返回连接后的字符串的方法join,其中每个字符串都将以指定的字符串分隔。
总结:
我们已经详细探讨了多种Python内建的数据结构。这些数据结构将是编写程序时至关重要的部分。
现在我们已经掌握了很多Python的基本知识,我们接下来将学习如何设计和编写一个实用的Python程序。