第三章 WEB开发

数据结构

数据结构基本上就是-它们是结构可容纳一些数据一起。换句话说,它们用于存储相关数据的集合。

Python中有四种内置数据结构 - 列表,元组,字典和集合。我们将看到如何使用它们以及它们如何让我们的生活更轻松。

名单

A list是一个包含有序项目集合的数据结构,即您可以在列表中存储一系列项目。这很容易想象,如果你能想到一个购物清单,你有一个要购买的物品清单,除了你可能在你的购物清单中的每个项目都在一个单独的行上,而在Python中你可以在它们之间加入逗号。

项目列表应括在方括号中,以便Python了解您正在指定列表。创建列表后,您可以添加,删除或搜索列表中的项目。由于我们可以添加和删除项目,因此我们说列表是可变数据类型,即此类型可以更改。

对象和类的快速简介

虽然到目前为止我一直在推迟对对象和类的讨论,但现在需要一点解释,这样你就可以更好地理解列表。我们将在后面的章节中详细探讨这个主题。

列表是对象和类的使用示例。当我们使用一个变量i和值分配给它,说整5它,你可以把它作为创建一个对象(即实例)i(即类型)int。事实上,你可以阅读help(int)以更好地理解这一点。

类也可以具有方法,即仅针对该类定义的函数。只有拥有该类的对象时,才能使用这些功能。例如,Python为类提供了一种append方法list,允许您将项添加到列表的末尾。例如,mylist.append('an item')将该字符串添加到列表中mylist。请注意使用点分表示法来访问对象的方法。

一个类也可以有一些字段,这些字段只是定义为仅针对该类使用的变量。只有拥有该类的对象时,才能使用这些变量/名称。例如,字段也可以用点分表示法访问mylist.field

示例(另存为ds_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)

输出:

$ python ds_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循环来遍历列表中的项目。到目前为止,您必须意识到列表也是一个序列。序列的特性将在后面的部分中讨论。

注意在函数end调用中使用参数print来指示我们想要用空格而不是通常的换行来结束输出。

接下来,我们使用appendlist对象的方法将一个项添加到列表中,如前所述。然后,我们通过简单地将列表传递给print打印整齐的功能,通过打印列表的内容来检查项目是否确实已添加到列表中。

然后,我们使用列表的sort方法对列表进行排序。重要的是要理解这个方法会影响列表本身并且不会返回修改后的列表 - 这与字符串的工作方式不同。这就是我们所说的列表是可变的并且字符串是不可变的

接下来,当我们完成在市场上购买商品时,我们希望将其从列表中删除。我们通过使用del声明来实现这一点。在这里,我们提到要删除的列表中的哪个项目,并且该del语句将其从列表中删除。我们指定要从列表中删除第一个项目,因此我们使用del shoplist[0](记住Python从0开始计数)。

如果您想知道列表对象定义的所有方法,请参阅help(list)详细信息。

元组

元组用于将多个对象组合在一起。可以认为它们与列表类似,但没有列表类为您提供的广泛功能。元组的一个主要特性是它们像字符串一样是不可变的,即你不能修改元组。

通过在可选的括号对中指定由逗号分隔的项来定义元组。

元组通常用于语句或用户定义函数可以安全地假设值集合(即使用的值的元组)不会改变的情况。

示例(另存为ds_using_tuple.py):

# I would recommend always using parentheses
# to indicate start and end of tuple
# even though parentheses are optional.
# Explicit is better than implicit.
zoo = ('python', 'elephant', 'penguin')
print('Number of animals in the zoo is', len(zoo))

new_zoo = 'monkey', 'camel', zoo    # parentheses not required but are a good idea
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]))

输出:

$ python ds_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通过指定访问第三个项目,并通过指定new_zoo[2]访问new_zoo元组中第三个项目中的第三个项目new_zoo[2][2]。一旦你理解了这个成语,这很简单。

元组有0或1个项目

空元组由一对空括号构成,例如myempty = ()。但是,具有单个项目的元组并非如此简单。您必须在第一个(也是唯一的)项后面使用逗号指定它,以便Python可以区分表达式中对象周围的元组和一对括号,即您必须指定singleton = (2 , )是否意味着您需要包含该项的元组2

注意Perl程序员

列表中的列表不会丢失其标识,即列表不会像在Perl中那样被展平。这同样适用于元组中的元组,列表中的元组或元组中的列表等。就Python而言,它们只是使用另一个对象存储的对象,这就是全部。

字典

字典就像地址簿,您可以通过仅知道他/她的名字来查找人的地址或联系方式,即我们将密钥(名称)与(详细信息)相关联。请注意,如果您有两个具有完全相同名称的人员,则密钥必须是唯一的,就像您无法找到正确的信息一样。

请注意,您只能使用不可变对象(如字符串)作为字典的键,但您可以使用不可变或可变对象作为字典的值。这基本上意味着你应该只使用简单的对象作为键。

通过使用表示法在字典中指定成对的键和值d = {key1 : value1, key2 : value2 }。请注意,键值对由冒号分隔,并且这些对用逗号分隔,所有这些都用一对花括号括起来。

请记住,字典中的键值对不以任何方式排序。如果您想要特定订单,那么在使用之前您必须自己对它们进行排序。

您将使用的词典是dict该类的实例/对象。

示例(另存为ds_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 {} contacts in the address-book\n'.format(len(ab)))

for name, address in ab.items():
    print('Contact {} at {}'.format(name, address))

# Adding a key-value pair
ab['Guido'] = 'guido@python.org'

if 'Guido' in ab:
    print("\nGuido's address is", ab['Guido'])

输出:

$ python ds_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方法访问字典的每个键值对,该字典返回元组列表,其中每个元组包含一对项 - 键后跟值。我们检索这一对并将其分配给变量,nameaddress相应地使用for..in循环将每个对分配给变量,然后在for-block中打印这些值。

我们可以通过简单地使用索引操作符来访问键并分配该值来添加新的键值对,就像我们在上面的例子中为Guido所做的那样。

我们可以使用in运算符检查键值对是否存在。

有关dict该类的方法列表,请参阅help(dict)

关键字参数和词典

如果您在函数中使用了关键字参数,则表示您已使用过字典!试想一下 - 键值对由你在函数定义的参数列表中指定,当你访问函数中的变量时,它只是一个字典的键访问(在编译器设计中称为符号表)术语)。

序列

列表,元组和字符串是序列的例子,但序列是什么以及它们的特殊之处是什么?

主要特点是成员的测试,(即innot in表达式)和索引操作,这使我们能够在序列中直接获取特定项目。

上面提到的三种类型的序列 - 列表,元组和字符串,也有一个切片操作,它允许我们检索序列的一个切片,即序列的一部分。

示例(另存为ds_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[:])

输出:

$ python ds_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解释器的交互式切片规范的各种组合,即提示,以便您可以立即查看结果。关于序列的好处是你可以以相同的方式访问元组,列表和字符串!

集是无序的简单对象的集合。当集合中对象的存在比顺序或其发生次数更重要时,可以使用这些。

使用集合,您可以测试成员资格,是否是另一个集合的子集,查找两个集合之间的交集,依此类推。

>>> 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'}

这个怎么运作

如果你还记得学校的基本集理论数学,那么这个例子就是不言自明的。但如果没有,你可以谷歌“集合论”和“维恩图”来更好地理解我们在Python中使用集合。

参考

当您创建对象并将其分配给变量时,该变量仅引用该对象,并不代表该对象本身!也就是说,变量名称指向存储对象的计算机内存部分。这称为将名称绑定到对象。

通常,您不必担心这一点,但由于您需要注意的引用,因此会产生微妙的影响:

示例(另存为ds_reference.py):

print('Simple Assignment')
shoplist = ['apple', 'mango', 'carrot', 'banana']
# mylist is just another name pointing to the same object!
mylist = shoplist

# I purchased the first item, so I remove it from the list
del shoplist[0]

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')
# Make a copy by doing a full slice
mylist = shoplist[:]
# Remove first item
del mylist[0]

print('shoplist is', shoplist)
print('mylist is', mylist)
# Notice that now the two lists are different

输出:

$ python ds_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']

这个怎么运作

大多数解释都在评论中提供。

请记住,如果要复制列表或此类序列或复杂对象(不是整数等简单对象),则必须使用切片操作进行复制。如果您只是将变量名称指定给另一个名称,那么它们都会“引用”同一个对象,如果您不小心,这可能会有问题。

注意Perl程序员

请记住,列表的赋值语句不会创建副本。您必须使用切片操作来制作序列的副本。

更多关于字符串

我们之前已经详细讨论了字符串。还有什么可以知道的?那么,你知道字符串也是对象,并且有从检查字符串的一部分到剥离空间的所有方法吗?事实上,你已经使用了一个字符串方法......这个format方法!

您在程序中使用的字符串都是该类的对象str。下一个示例演示了此类的一些有用方法。有关此类方法的完整列表,请参阅help(str)

示例(另存为ds_str_methods.py):

# This is a string object
name = 'Swaroop'

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))

输出:

$ python ds_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方法用于定位字符串中给定子字符串的位置; find如果找不到子字符串,则返回-1。的str类也有一个整洁的方法join与字符串充当序列的每个项之间的分隔符序列的项,并返回来自该产生的更大的字符串。

摘要

我们已经详细探讨了Python的各种内置数据结构。这些数据结构对于编写合理大小的程序至关重要。

现在我们已经掌握了很多Python的基础知识,接下来我们将看到如何设计和编写真实的Python程序。

Last updated