【练习题】第十章--列表(Think Python)

列表

在列表里面,这些值可以是任意类型的。一个列表中的值一般叫做列表的元素,有时候也叫列表项。

列表内部可以包含一个列表作为元素,这种包含列表的列表也叫做网状列表:

['spam', 2.0, 5, [10, 20]]

列表元素可修改

和字符串不同的是,列表是可以修改的。

列表的索引和字符串的索引的格式是一样的:

• 任意的一个整型表达式,都可以用来作为索引编号。

• 如果你试图读取或者写入一个不存在的列表元素,你就会得到一个索引错误 IndexError。

• 如果一个索引是负值,意味着是从列表末尾向前倒序计数查找相对应的位置。

在列表中也可以使用 in 运算符。

>>> cheeses = ['Cheddar', 'Edam', 'Gouda']
>>> 'Edam' in cheeses
True
>>> 'Brie' in cheeses
False

遍历一个列表

遍历一个列表中所有元素的最常用的办法就是 for 循环了。这种 for 循环和我们在遍历一个字符串的时候用的是一样的的语法:

for cheese in cheeses:
    print(cheese)

如果你只是要显示一下列表元素,上面这个代码就够用了。但如果你还想写入或者更新这些元素,你还是需要用索引。一般来说,这需要把两个内置函数 range 和 len 结合起来使用:

for i in range(len(numbers)):
    numbers[i] = numbers[i] * 2

空列表的 for 循环中,循环体是永远不会运行的:

for x in []:
    print('This never happens.')

尽管列表中可以办好另外一个列表,但这种网状的分支列表依然只会被算作一个元素。所以下面这个列表的长度是4:

['spam', 1, ['Brie', 'Roquefort', 'Pol le Veq'], [1, 2, 3]]

列表运算符

+、*------和字符串的一样

列表切片

在切片运算中,如果你省略了第一个索引,切片就会从头开始。如果省略了第二个,切片就会一直走到末尾。所以如果你把两个都省略了,这个切片就是整个列表的一个复制了。

>>> t[:]
['a', 'b', 'c', 'd', 'e', 'f']

列表的方法

t.append():可以在列表末尾添加一个新的元素

t.extend():使用另一个列表做参数,然后把所有的元素添加到一个列表上

t.sort():把列表中的元素从低到高(译者注:下面的例子中是按照 ASCII 码的大小从小到大)排列

Map, filter, reduce 列表中最重要的三种运算

reduce:

把一系列列表元素组合成一个单值的运算,也叫做 reduce(这个单词是缩减的意思)。

sum(t):将t列表中所有元素加起来

>>> t = [1, 2, 3]
>>> sum(t)
6

map:

将某一函数(该例子中是 capitalize 这个方法)应用到一个序列中的每个元素上。

def capitalize_all(t):
    res = []
    for s in t:
        res.append(s.capitalize())#capitalize()-转换为大写
    return res

 filter:

从列表中选取一些元素,然后返回一个次级列表。

比如,下面的函数接收一个字符串列表,然后返回一个其中只包含大写字母的字符串所组成的列表:

def only_upper(t):
    res = []
    for s in t:
        if s.isupper():
            res.append(s)
    return res

isupper 是一个字符串方法,如果字符串中只包含大写字母就会返回真。

only_upper 这样的运算也叫 filter(过滤器的意思),因为这种运算筛选出特定的元素,过滤掉其他的。

常用的列表运算都可以表示成 map、filter 以及 reduce 的组合。

删除元素

从一个列表中删除元素有几种方法。如果你知道你要删除元素的索引,你就可以用 pop这个方法来实现:

>>> t = ['a', 'b', 'c']
>>> x = t.pop(1)
>>> t
['a', 'c']
>>> x
'b'

pop 修改列表,然后返回删除的元素。如果你不指定一个索引位置,pop 就会删除和返回最后一个元素。

如果你不需要删掉的值了,你可以用 del 运算符来实现:

>>> t = ['a', 'b', 'c']
>>> del t[1]
>>> t
['a', 'c']

如果你知道你要删除的元素值,但不知道索引位置,你可以使用 remove 这个方法:

>>> t = ['a', 'b', 'c']
>>> t.remove('b')
>>> t
['a', 'c']

remove 的返回值是空。

要删除更多元素,可以使用 del 和切片索引

>>> t = ['a', 'b', 'c', 'd', 'e', 'f']
>>> del t[1:5]
>>> t
['a', 'f']

和之前我们看到的一样,切片是含头不含尾,上面这个例子中从第『1』到第『5』个都被切片所选中,但包含开头的第『1』而不包含末尾的第『5』个元素。

列表和字符串

字符串是一系列字符的序列,而列表是一系列值的序列,但一个由字符组成的列表是不同于字符串的。要把一个字符串转换成字符列表,你可以用 list 这个函数:

>>> s = 'spam'
>>> t = list(s)
>>> t
['s', 'p', 'a', 'm']

 想把字符串切分成一个个单词,你可以用 split 这个方法:

>>> s = 'pining for the fjords'
>>> t = s.split()
>>> t
['pining', 'for', 'the', 'fjords']

可选的参数是定界符,是用来确定单词边界的。下面这个例子中就是把连接号『-』作为定界符:

>>> s = 'spam-spam-spam'
>>> delimiter = '-'
>>> t = s.split(delimiter)
>>> t
['spam', 'spam', 'spam']

join 是与 split 功能相反的一个方法。它接收一个字符串列表,然后把所有元素拼接到一起。join 是一个字符串方法,所以必须把 join 放到定界符后面来调用,并且传递一个列表作为参数

>>> t = ['pining', 'for', 'the', 'fjords']
>>> delimiter = ' '
>>> s = delimiter.join(t)
>>> s
'pining for the fjords'

上面这个例子中,定界符是一个空格字符,所以 join 就在单词只见放一个空格。要想把字符聚集到一切而不要空格,你就可以用空字符串""作为一个定界符了。

对象和值

要检查两个变量是否指向的是同一个对象,可以用 is 运算符。

>>> a = 'banana'
>>> b = 'banana'#a和b指向两个不同的对象,这两个对象有相同的值。在第二个情况下,a 和 b 都指向同一个对象。
>>> a is b
True

当建立两个列表时,得到的就是两个对象:

>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> a is b
False

一个对象拥有一个值。如果你计算[1,2,3],你得到一个列表对象,整个列表对象的整个值是一个整数序列。如果另外一个列表有相同的元素,我们称之含有相同的值,但并不是相同的对象。 

别名

如果 a 是一个对象了,然后你赋值 b=a,那么这两个变量都指向同一个对象:

>>> a = [1, 2, 3]
>>> b = a
>>> b is a
True

这两个变量亦成为对象的引用。所以对其中一个引用的修改,会使得对象发生改变,进而使得另一个引用(即别名)发生改变:

>>> b[0] = 42
>>> a
[42, 2, 3]

列表参数 

当你传递一个列表给一个函数的时候,函数得到的是对该列表的一个引用。如果函数修改了列表,调用者会看到变化的。比如下面这个 delete_head 函数就从列表中删除第一个元素:

def delete_head(t):
    del t[0]

其用法如下:

>>> letters = ['a', 'b', 'c']
>>> delete_head(letters)
>>> letters ['b', 'c']

形式参数 t 和变量 letters 都是同一对象的别名。

一定要区分修改列表的运算和产生新列表的运算,这特别重要。比如 append 方法修改一个列表,但加号+运算符是产生一个新的列表

>>> t1 = [1, 2]
>>> t2 = t1.append(3)
>>> t1
[1, 2, 3]
>>> t2
None

append 修改了列表,返回的是空。

>>> t3 = t1 + [4]
>>> t1
[1, 2, 3]
>>> t3
[1, 2, 3, 4]
>>> t1
[1, 2, 3]

加号+运算符创建了新的列表,并不修改源列表。

加以区别相当重要,尤其是当你写一些要修改列表的函数的时候。比如下面这个函数并没有能够删除列表的第一个元素:

def bad_delete_head(t):
    t = t[1:]              # WRONG!

切片运算符创建了新的列表,然后赋值语句让 t 指向了这个新列表,但这不会影响调用者:

>>> t4 = [1, 2, 3]
>>> bad_delete_head(t4)
>>> t4
[1, 2, 3]

在bad_delete_head这个函数开始运行的时候,t 和 t4指向同一个列表。在结尾的时候,t 指向了新的列表(此列表为t4[1:]),但 t4依然还是原来那个列表,而且没修改过。

一种替代方法是写一个能创建并返回新列表的函数。比如 tail 这个函数就返回列表除了首个元素之外的其他所有元素:

def tail(t):
    return t[1:]

这个函数会将源列表保持原样不做修改。下面是用法:

>>> letters = ['a', 'b', 'c']
>>> rest = tail(letters)
>>> rest
['b', 'c']

 调试

1. 大多数列表方法都修改参数,返回空值。这正好与字符串方法相反,字符串的方法都是返回一个新字符串,保持源字符串不变。

如果你习惯些下面这种字符串代码了:

word = word.strip()

你估计就会写出下面这种列表代码:

t = t.sort()           # WRONG!

因为 sort 返回的是空值,所以对 t 的后续运算都将会失败。

在使用列表的方法和运算符之前,你应该好好读一下文档,然后在交互模式里面对它们进行一下测试。

2. 选一种方法并坚持使用。

列表使用的问题中很大一部分都是因为有太多方法来实现目的导致的。例如要从一个列表中删除一个元素,可以用 pop,remove,del 甚至简单的切片操作。

要加一个元素,可以用 append 方法或者加号+运算符。假设 t 是一个列表,而 x 是一个列表元素,下面的代码都是正确的:

t.append(x)
t = t + [x]
t += [x]

下面这就都是错的了:

t.append([x])          # WRONG!
t = t.append(x)        # WRONG!
t + [x]                # WRONG!
t = t + x              # WRONG!

在交互模式下试试上面这些例子,确保你要理解它们的作用。要注意只有最后一个会引起运行错误,其他的三个都是合法的,但产生错误的效果。

3. 尽量做备份,避免用别名。

如果你要用 sort 这样的方法来修改参数,又要同时保留源列表,你可以先做个备份。

>>> t = [3, 1, 2]
>>> t2 = t[:]
>>> t2.sort()
>>> t
[3, 1, 2]
>>> t2
[1, 2, 3]

在这个例子中,你也可以用内置函数sorted,这个函数会返回一个新的整理过的列表,而不会影响源列表。

>>> t2 = sorted(t)
>>> t
[3, 1, 2]
>>> t2
[1, 2, 3]

练习1 :

写一个函数,名为 nested_sum,接收一系列的整数列表,然后把所有分支列表中的元素加起来。如下所示:

>>> t = [[1, 2], [3], [4, 5, 6]]
>>> nested_sum(t)
21

mydode (我这个厉害一点,任何整数型网状列表都可以累加):

def nested_sum(t):
    a=t[:]
    for i in range(len(a)):
        if isinstance(a[i],list):
             a[i]=nested_sum(a[i])
    return sum(a)

t=[[1,2],[1,2],2,3]
print(nested_sum(t))

练习2:

写一个函数,明切 cumsum,接收一个数字列表,然后返回累加的总和;也就是新列表的第 i 个元素就是源列表中前 i+1个元素的累加。如下所示:

>>> t = [1, 2, 3]
>>> cumsum(t)
[1, 3, 6]

 mycode:

def cumsum(t):
    for i in range(len(t)):
        t[i]=sum(t[:i+1])

练习3 :

写一个函数,名为 middle,接收一个列表,返回一个新列表,新列表要求对源列表掐头去尾只要中间部分。如下所示:

>>> t = [1, 2, 3, 4]
>>> middle(t)
[2, 3]

mycode:

def middle(t):
    n=len(t)
    return t[1:(n-1)]

练习4:

写一个函数,名为 chop,接收一个列表,修改这个列表,掐头去尾,返回空值。如下所示:

>>> t = [1, 2, 3, 4]
>>> chop(t)
>>> t
[2, 3]

 mycode:

def chop(t):
    del t[0]
    del t[-1]
    return None

练习5:

写一个函数,名为 is_sorted,接收一个列表作为参数,如果列表按照字母顺序升序排列,就返回真,否则返回假。如下所示:

>>> is_sorted([1, 2, 2])
True
>>> is_sorted(['b', 'a'])
False

mycode:

def is_sorted(t):
    a=t[:]
    a.sort()
    if t==a:
        return True
    else:
        return False

练习6:

两个单词如果可以通过顺序修改来互相转换就互为变位词。写一个函数,名为 is_anagram,接收两个字符串,如果互为变位词就返回真

def is_anagram(word1,word2):
    word1_dup=list(word1)
    word2_dup=list(word2)
    word1_dup.sort()
    word2_dup.sort()
    if word1_dup==word2_dup:
        return True
    else:
        return False

练习7:

写一个函数,名为 has_duplicates,接收一个列表,如果里面有重复出现的元素,就返回真。这个函数不能修改源列表。

mycode:

def has_duplicates(t):
    for i in t:
        if t.count(i)>1:
            return True
    return False

附一个大神的总结:Python统计列表元素出现次数--https://blog.csdn.net/weixin_40604987/article/details/79292493

练习8:

假如你班上有23个学生,这当中两个人同一天出生的概率是多大?你可以评估一下23个随机生日中有相同生日的概率。提示一下:你可以用 randint 函数来生成随机的生日,这个函数包含在 random 模块中。

mycode:

def random_bdays(n):
    t=[]
    for i in range (n):
        bday=random.randin(1,365)
        t.append(bday)
    return t

andanswer:

def count_matches(num_students, num_simulations):
    """Generates a sample of birthdays and counts duplicates.

    num_students: how many students in the group
    num_samples: how many groups to simulate

    returns: int
    """
    count = 0
    for i in range(num_simulations):
        t = random_bdays(num_students)
        if has_duplicates(t):
            count += 1
    return count


def main():
    """Runs the birthday simulation and prints the number of matches."""
    num_students = 23
    num_simulations = 1000
    count = count_matches(num_students, num_simulations)

    print('After %d simulations' % num_simulations)
    print('with %d students' % num_students)
    print('there were %d simulations with at least one match' % count)


练习9:

写一个函数,读取文件 words.txt,然后建立一个列表,这个列表中每个元素就是这个文件中的每个单词。写两个版本的这样的函数,一个使用 append 方法,另外一个用自增运算的模式:t= t + [x]。看看哪个运行时间更长?为什么会这样?

mycode:

import time
def read_append(txt):
    fin=open(txt)
    t=[]
    for word in fin:
        t.append(word.strip())
    return t

def read_incre(txt):
    fin=open(txt)
    t=[]
    for word in fin:
        t=t+[word.strip()]
    return t

txt='words.txt'
fin=open(txt)
t0=time.time()
read_append(txt)
print('time for append:',(time.time()-t0))
t1=time.time()
read_incre(txt)
print('time for incre:',(time.time()-t1))

原因解析:

append()  :向列表尾部追加一个新元素,列表只占一个索引位,在原有列表上增加。

+  :实际上是生成了一个新的列表存这两个列表的和,只能用在两个列表相加上。所以它的耗时需要很长!

+=:和append()差不多,但慢一些

练习10:

要检查一个单词是不是在上面这个词汇列表里,你可以使用 in 运算符,但可能会很慢,因为这个 in 运算符要从头到尾来搜索整个词汇表。

我们知道这些单词是按照字母表顺序组织的,所以我们可以加速一下,用一种对折搜索(也叫做二元搜索),这个过程就和你在现实中用字典来查单词差不多。你在中间部分开始,看看这个要搜索的词汇是不是在中间位置的前面。如果在前面,就又对前半部分取中间,继续这样来找。当然了,不在前半部分,就去后半部分找了,思路是这样的。

不论怎样,每次都会把搜索范围缩减到一半。如果词表包含了113809个单词,最多就是17步就能找到单词,或者能确定单词不在词汇表中。

那么问题来了,写一个函数,名为 in_bisect,接收一个整理过的按照字母顺序排列的列表,以及一个目标值,在列表中查找这个值,找到了就返回索引位置,找不到就返回空。

mycode:

def in_bisect(t,word):
    first=0
    last=len(t)-1
    print('last is '+str(last))
    i=int((first+last)/2)
    count=0
    while word!=t[i]:
        if i==first:
            return 'None'
        if word<t[i]:
            last=i
            i=int((first+last)/2)
        elif word>t[i]:
            first=i
            i=int((first+last)/2)
        count=count+1
    return i

fin=open('words.txt')
words=fin.read()
t=words.split('\n')
word='zz'
print('the word--'+word+' is '+str(in_bisect(t,word)))

练习11:

两个词如果互为逆序,就称它们是『翻转配对』。写一个函数来找一下在这个词汇表中所有这样的词对。

def reverse_pair(word_list, word):
    rev_word = word[::-1]
    return in_bisect(word_list, rev_word)
    
fin=open('words.txt')
t=(fin.read()).split('\n')
for word in t:
    if reverse_pair(t,word)!='None':
        print(word)

练习12:

两个单词,依次拼接各自的字母,如果能组成一个新单词,就称之为『互锁』。比如,shoe 和 cold就可以镶嵌在一起组成组成 schooled。(译者注:shoe+cold= schooled样例代码. 说明:这个练习受到了这里一处例子的启发。

  1. 写一个程序,找到所有这样的互锁单词对。提示:不要枚举所有的单词对!

  2. 你能找到那种三路互锁的单词么;就是那种三三排列三个单词的字母能组成一个单词的三个词?

猜你喜欢

转载自blog.csdn.net/qq_29567851/article/details/83149866