《Python 快速入门(第3版)》娜奥米·赛德
4.3 变量和赋值
Python中的变量不是容器,而是指向Python对象的标签,对象位于解释器的命名空间中。任意数量的标签(或变量)可以指向同一个对象。当对象发生变化时,所有指向它的变量的值都会改变。
新的赋值操作会覆盖之前所有的赋值,del语句则会删除变量。如果在删除变量之后设法输出该变量的内容,将会引发错误,效果就像从未创建过该变量一样:
- >>> x = 5
- >>> print(x)
- 5
- >>> del x
- >>> print(x)
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- NameError: name 'x' is not defined
- >>>
4.7 None值
检测None是否存在十分简单,因为在整个Python系统中只有1个None的实例,所有对None的引用都指向同一个对象,None只等价于它自身。
4.8 获取用户输入
利用input()函数可以获取用户的输入。input()函数可以带一个字符串参数,作为显示给用户的提示信息。
用户输入是以字符串的形式获得的,所以要想用作数字,必须用int()或float()函数进行转换。
4.10 基本的Python编码风格
A.3.2 代码布局
1.缩进
每级缩进采用4个空格
3.最大行长
所有行都应限制在79个字符以内。
对于连续的大段文字(文档字符串或注释),建议将行长限制在72个字符以内。
长行进行换行的首选方案,是利用Python隐含的行连接特性,在圆括号、方括号和大括号内部进行断行。必要时可以在表达式外面多加一对圆括号,不过有时候用反斜杠会更好看些。请确保对后续行进行适当的缩进。打断二元运算符的首选位置是在运算符之后。
4.空行
顶级函数和类定义之间,请用两个空行分隔。
类内部的各个方法定义之间,请用1个空行分隔。
5.
导入语句通常应单独成行,例如
- import os
- import sys
不过下面的写法没有问题:
from subprocess import Popen, PIPE
导入语句应按照以下顺序进行分组。
(1)标准库的导入。
(2)相关第三方库的导入。
(3)本地应用程序/库——特定库的导入。
每组导入语句之间请加入1个空行。
任何对应的__all__声明都应位于导入语句之后。
非常不推荐对内部包的导入使用相对导入语法。请始终对所有导入都使用绝对包路径。
6.表达式和语句内的空白符
以下场合应避免使用多余的空白符。
紧靠小括号、中括号或大括号内部。
紧挨着逗号、分号或冒号之前。
7.其它建议
始终在以下二元操作符两侧各放1个空格:赋值(=)、增量赋值(+=,-=等)、比较(==、<、>、!=、<>、<=、>=、in、not、in、is、is not)、布尔(and、or、not)。>> 在数学运算符两侧放置空格。
- i = i + 1
- submitted += 1
-
- x = x * 2-1
- hypot2 = x * x + y * y
- c = (a + b) * (a - b)
在用于指定关键字参数或默认参数值时,请勿在=两边使用空格。
- def complex(real, imag=0.0):
- return magic(r=real, i=imag)
通常不鼓励使用复合语句,也就是在同一行放置多条语句。
5.2 列表的索引机制
如果第二个索引给出一个第一个索引之前的位置,会返回空列表;提供步长为-1可以逆序截取
- >>> x = ["first","second","third","fourth"]
- >>> x[-1:2]
- []
- >>> x[-1:2:-1]
- ['fourth']
- >>>
如果两个索引都省略了,可实现列表复制。对该列表副本修改不会影响原列表
- >>> x
- ['first', 'second', 'third', 'fourth']
- >>>
- >>> y = x[:]
- >>> y[0] = '1st'
- >>> y
- ['1st', 'second', 'third', 'fourth']
- >>> x
- ['first', 'second', 'third', 'fourth']
- >>>
5.3 修改列表
切片语法也可以这样使用。类似lista[index1:index2]=listb的写法,会导致lista在index1和index2之间的所有元素都被listb的元素替换掉。listb的元素数量可以多于或少于lista中被移除的元素数,这时lista的长度会自动做出调整。
利用切片赋值操作,可以实现很多功能,例如
(1)在列表末尾追加列表
- >>> x = [1, 2, 3, 4]
- >>> x[len(x):] = [5, 6, 7]
- >>> x
- [1, 2, 3, 4, 5, 6, 7]
- >>>
(2)在列表开头插入列表
- >>> x[:0] = [-1, 0]
- >>> x
- [-1, 0, 1, 2, 3, 4, 5, 6, 7]
- >>>
(3)移除列表元素
- >>> x[2:-2] = []
- >>> x
- [-1, 0, 6, 7]
- >>>
extend方法和append方法类似,但是它能够将列表追加到另一个列表之后
- >>> y
- ['1st', 'second', 'third', 'fourth']
- >>> x.extend(y)
- >>> x
- [-1, 0, 6, 7, '1st', 'second', 'third', 'fourth']
- >>>
进行了如下尝试
x.extend(1) --> 会报错,需要iterable的数据类型
x.extend('1') --> OK
x.extend([1]) --> OK
x.append('1') --> OK
x.append(1) --> OK
- >>> x.extend(1)
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- TypeError: 'int' object is not iterable
- >>>
大多数情况下,可将list.insert(n, elem)简单地理解为,在列表的第n个元素之前插入elem。
当n是非负值时,list.insert(n, elem)与list[n:n] = [elem]的效果是一样的。
删除列表数据项或切片的推荐方法是使用del语句。
通常,del list[n]的功能与list[n:n+1] = []是一样的,而del list[m:n]的功能则与list[m:n] = []相同。
remove则会先在列表中查找给定值的第一个实例,然后将该值从列表中删除;
如果remove找不到要删除的的值,就会引发错误。
列表的reverse方法是一种较为专业的列表修改方法,可以高效地将列表逆序
动手题:将列表最后3个数据项从列表的末尾移到开头,并保持顺序不变
- >>> x
- [-1, 0, 6, 7, '1st', 'second', 'third', 'fourth']
- >>>
- >>> x[:0] = x[-3:]
- >>> x
- ['second', 'third', 'fourth', -1, 0, 6, 7, '1st', 'second', 'third', 'fourth']
- >>> x[-3:] = []
- >>> x
- ['second', 'third', 'fourth', -1, 0, 6, 7, '1st']
- >>>
5.4 对列表排序
sort方法会按排序修改列表。如果排序时不想修改原列表,可以有两种做法:
一种是使用内置的sorted()函数;另一种是先建立列表的副本,再对副本进行排序
- >>> x = [2, 4, 1, 3]
- >>> y = x[:]
- >>> y.sort()
- >>> y
- [1, 2, 3, 4]
- >>> x
- [2, 4, 1, 3]
- >>> sorted(x)
- [1, 2, 3, 4]
- >>> x
- [2, 4, 1, 3]
- >>>
sort方法可以带有可选的reverse参数,当reverse=True时可以实现逆向排序
- >>> y.sort(reverse=True)
- >>> y
- [4, 3, 2, 1]
- >>>
sort方法还可以用自定义的键函数来决定列表元素的顺序
- >>> def compare_num_of_chars(string1):
- ... return len(string1)
- ...
- >>> word_list = ['Python', 'Java', 'PHP', 'Go', 'C++']
- >>> word_list.sort()
- >>> word_list
- ['C++', 'Go', 'Java', 'PHP', 'Python']
- >>> word_list.sort(key=compare_num_of_chars)
- >>> word_list
- ['Go', 'C++', 'PHP', 'Java', 'Python']
- >>>
动手题:列表排序假设有个列表的元素也都是列表:[[1, 2, 3], [2, 1, 3], [4, 0, 1]]。如果要按每个子列表的第二个元素对列表排序,结果应该为[[4, 0, 1], [2, 1, 3], [1, 2, 3]],那么该如何为sort()方法的key参数编写函数呢?
- >>> def compare_2nd_ele(list1):
- ... return list1[1]
- ...
- >>> testlist = [[1, 2, 3],[2, 1, 3],[4, 0, 11]]
- >>> testlist.sort(key=compare_2nd_ele)
- >>> testlist
- [[4, 0, 11], [2, 1, 3], [1, 2, 3]]
- >>>
5.5 其他常用的列表操作
用*操作符初始化列表
- >>> z = [None] * 4
- >>> z
- [None, None, None, None]
- >>>
用index方法搜索列表;在调用index方法之前,用in对列表进行测试。
- >>> x
- [2, 4, 1, 3]
- >>>
- >>> x.index(4)
- 1
- >>> x.index(5)
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- ValueError: 5 is not in list
- >>>
- >>> 5 in x
- False
- >>> 4 in x
- True
- >>>
用count方法对匹配项计数
count也会遍历列表并查找给定值,但返回的是在列表中找到该值的次数
5.6 嵌套列表和深复制
通过全切片(即x[:])可以得到列表的副本,用+或*操作符(如x+[]或x*1)也可以得到列表的副本。但它们的效率略低于使用切片的方法。这3种方法都会创建所谓的浅副本(shallow copy)
如果列表中有嵌套列表,那就可能需要深副本(deep copy)。深副本可以通过copy模块的deepcopy函数来得到,深副本完全与原变量无关,其变化对原列表没有影响。
- >>> original = [[0],1]
- >>> shallow = original[:]
- >>>
- >>> import copy
- >>> deep = copy.deepcopy(original)
- >>>
- >>> shallow[1] = 2
- >>> shallow
- [[0], 2]
- >>>
- >>> original
- [[0], 1]
- >>> shallow[0][0] = 'zero'
- >>> original
- [['zero'], 1]
- >>>
- >>> deep[0][0] = 5
- >>> deep
- [[5], 1]
- >>>
- >>> original
- [['zero'], 1]
- >>>
5.7 元组
元组副本的创建方式,与列表完全相同:
交换两个变量的值
var1, var2 = var2, var1
- >>> a = 1
- >>> b = 2
- >>> a, b = b, a
- >>> a
- 2
- >>> b
- 1
- >>>
列表和元组的相互转换
- >>> x
- [2, 4, 1, 3]
- >>> y
- [1, 2, 3, 4]
- >>>
- >>>
- >>> x
- [2, 4, 1, 3]
- >>> tuple(x)
- (2, 4, 1, 3)
- >>>
- >>> x
- [2, 4, 1, 3]
- >>> y=tuple(x)
- >>>
- >>> list(y)
- [2, 4, 1, 3]
- >>>
用list,很容易就能将字符串拆分为字符
- >>> list('Kubernetes')
- ['K', 'u', 'b', 'e', 'r', 'n', 'e', 't', 'e', 's']
- >>>
5.8 集合
集合中的项必须是不可变的、可散列的。这就表示,整数、浮点数、字符串和元组可以作为集合的成员,但列表、字典和集合本身不可以。
通过对序列(如列表)调用set函数,可以创建集合,重复的元素将会被移除。
用set函数创建集合后,可以用add和remove修改集合中的元素。
关键字in可用于检查对象是否为集合的成员。
用操作符“|”可获得两个集合的并集;
用操作符“&”可获得交集;
用操作符“^”则可以求得对称差(symmetric difference),对称差是指,属于其中一个但不同时属于两个集合的元素。
集合类型frozenset,是不可变的、可散列的,因此可以作为其他集合的成员
6.4 字符串方法
6.4.1 字符串的split和join方法
- >>> "::".join(["Separated", "with", "colons"])
- 'Separated::with::colons'
- >>>
- >>> 'Separated::with::colons'.split(":")
- ['Separated', '', 'with', '', 'colons']
- >>> 'Separated::with::colons'.split("::")
- ['Separated', 'with', 'colons']
- >>>
- >>>
- >>> '123 4 5 7 8 /t 999'.split()
- ['123', '4', '5', '7', '8', '/t', '999']
- >>>
- >>> '123 4 /n 5 7/n 8/t 999'.split()
- ['123', '4', '/n', '5', '7/n', '8/t', '999']
- >>>
可以用空字符串""来拼接字符串列表的元素:
- >>> "".join(["Separated", "by", "nothing"])
- 'Separatedbynothing'
- >>>
通过给split方法传入第二个可选参数来指定生成结果时执行拆分的次数。假设指定要拆分n次,则split方法会对输入字符串从头开始拆分,要么执行n次后停止拆分(此时生成的列表中包含n+1个子字符串)
如果既要用到第二个参数,又要按照空白符进行拆分,请将第一个参数设为None。
- >>> x
- [2, 4, 1, 3]
- >>>
- >>> x = 'a b c d'
- >>> x.split(' ',2)
- ['a', 'b', 'c d']
- >>> x.split(None,2)
- ['a', 'b', 'c d']
- >>>
6.4.3 去除多余的空白符
- >>> x = " Hello, World\t \t "
- >>> x
- ' Hello, World\t \t '
- >>> x.strip()
- 'Hello, World'
- >>> x
- ' Hello, World\t \t '
- >>>
- >>> x.lstrip()
- 'Hello, World\t \t '
- >>> x.rstrip()
- ' Hello, World'
- >>>
strip、rstrip和lstrip方法还可以附带一个参数,这个参数包含了需要移除的字符。是从外向内匹配删除,直到首/尾不再包含任意指定的字符。
- >>> x = "asdf Hello, World RRR"
- >>> x.strip('R')
- 'asdf Hello, World '
- >>> x.lstrip('sdfaR')
- ' Hello, World RRR'
- >>> x.rstrip('sdfaR ')
- 'asdf Hello, Worl'
- >>>
6.4.4 字符串搜索
基础的字符串搜索方法有4个,即find、rfind、index和rindex,它们比较类似。
find方法将会返回子字符串第一个实例的首字符在调用字符串对象中的位置,如果未找到子串则返回-1
find方法还可以带一或两个可选参数。第一个可选参数(如果存在)start是个整数,会让find在搜索子字符串时忽略字符串中位置start之前的所有字符。第二个可选参数(如果存在)end也是整数,会让find忽略位置end之后(含)的字符:
rfind方法的功能与find方法几乎完全相同,但是从字符串的末尾开始搜索,返回的是子字符串在字符串中最后一次出现时的首字符位置
当index或rindex方法(功能分别与find和rfind相同)在字符串中找不到子字符串时,不会返回-1,而是会引发ValueError。
startswith和endswith方法可以一次搜索多个子字符串。如果参数是个字符串元组,那么这两个方法就会对元组中的所有字符串进行检测,只要有一个字符串匹配就会返回True
- >>> x = "Mississippi"
- >>> x.startswith('Miss')
- True
- >>> x.startswith('miss')
- False
- >>> x.startswith(('miss','Miss'))
- True
- >>>
6.4.5 字符串修改
用replace方法可以将字符串中的子字符串(第一个参数)全部替换为另一个字符串(第二个参数)。
- >>> x.replace('ss','++')
- 'Mi++i++ippi'
- >>>
函数string.maketrans和string.translate可以配合起来使用,将字符串中的多个字符转换为其他字符。
- >>> x = "~x ^ (y % z)"
- >>> table = x.maketrans("~^()", "!&[]")
- >>> x.translate(table)
- '!x & [y % z]'
- >>>
第二行代码用maketrans构建了翻译对照表,数据来自其两个字符串参数。这两个参数的字符数必须相同。然后maketrans生成的对照表传给了translate方法。
判断字符串是否为数字、字母等
- >>> x = "123"
- >>> x.isalpha()
- False
- >>> x.isdigit()
- True
- >>>
- >>> y = "ZH"
- >>> y.islower()
- False
- >>> y.isupper()
- True
- >>>
动手题:字符串操作假设有一个字符串列表x,其中有一些字符串(不一定是全部)是以双引号开头和结尾的:x = ['"abc"', 'def', '"ghi"', '"klm"', 'nop'] ,该用什么代码遍历所有元素并只把双引号去除呢?
- >>> x
- ['"abc"', 'def', '"ghi"', '"klm"', 'nop']
- >>>
- >>>
- >>> y = []
- >>> for i in x:
- ... i = i.strip('"')
- ... y.append(i)
- ...
- >>> y
- ['abc', 'def', 'ghi', 'klm', 'nop']
- >>>
如何查找"Mississippi"中最后一个字母p的位置?找到后又该如何只去除该字母呢?
方法一:
- >>> if x.rfind('p') != -1:
- ... y = list(x)
- ... y.pop(x.rfind('p'))
- ... x = "".join(y)
- ...
- 'p'
- >>> x
- 'Mississipi'
- >>>
方法二:
- >>> x = 'Mississippi'
- >>> loc = x.rfind('p')
- >>> if loc != -1:
- ... x = x[:loc] + x[loc+1:]
- ...
- >>> x
- 'Mississipi'
- >>>
方法三(只回答了第二问):
- >>> x
- 'Mississippi'
- >>>
- >>> x_reverse = x[::-1]
- >>> x_reverse
- 'ippississiM'
- >>>
- >>> x_reverse_list = x_reverse.split('p',1)
- >>> x_reverse_list
- ['i', 'pississiM']
- >>>
- >>> x = (''.join(x_reverse_list))[::-1]
- >>> x
- 'Mississipi'
- >>>
6.5 将对象转换为字符串
用到Python面向对象的特性之前,repr和str没有区别。为了维持良好的编程风格,请渐渐习惯用str而不是repr来创建用于显示的字符串信息。
- >>> repr(len)
- '<built-in function len>'
- >>> repr([1,2,3,4])
- '[1, 2, 3, 4]'
- >>>
- >>> str(len)
- '<built-in function len>'
- >>> str([1,2,3,4])
- '[1, 2, 3, 4]'
- >>>
6.6 使用format方法
format方法用了两个参数,同时给出了包含被替换字段的格式字符串,以及替换后的值。这里的被替换字段是用{}标识的。如果要在字符串中包含字符“{”或“}”,请用“{{”或“}}”来表示。
6.6.1 format方法和位置参数
用被替换字段的编号,分别对应传入的参数
- >>> "{0} is {1} of {2}".format("Today","28th","June")
- 'Today is 28th of June'
- >>>
6.6.2 format方法和命名参数
- >>> "Today is {day} of {month}".format(month="June",day="28th")
- 'Today is 28th of June'
- >>>
同时使用位置参数和命名参数也是允许的,甚至可以访问参数中的属性和元素
- >>> "{0} is {day} of {month[5]} ({1})".format("Today","Tuesday",day="28th",month=["Jan","Feb","Mar","Apr","May","June","July"])
- 'Today is 28th of June (Tuesday)'
- >>>
注意:位置参数要写在命名参数前面,否则会报错
- >>> "{0} is {day} of {month[5]} ({1})".format("Today",day="28th",month=["Jan","Feb","Mar","Apr","May","June","July"],"Tuesday")
- File "<stdin>", line 1
- "{0} is {day} of {month[5]} ({1})".format("Today",day="28th",month=["Jan","Feb","Mar","Apr","May","June","July"],"Tuesday")
- ^
- SyntaxError: positional argument follows keyword argument
- >>>
6.6.3 格式描述符
- >>> "{0:10} is {1} of {2}".format("Today","28th","June")
- 'Today is 28th of June'
- >>>
- >>> "{0:{3}} is {1} of {2}".format("Today","28th","June",10)
- 'Today is 28th of June'
- >>>
- >>> "{0:{width}} is {1} of {2}".format("Today","28th","June",width=10)
- 'Today is 28th of June'
- >>> "{0:>10} is {1} of {2}".format("Today","28th","June")
- ' Today is 28th of June'
- >>> "{0:#>10} is {1} of {2}".format("Today","28th","June")
- '#####Today is 28th of June'
- >>> "{0:#<10} is {1} of {2}".format("Today","28th","June")
- 'Today##### is 28th of June'
- >>>
描述符“:10”设置该字段宽度为10个字符,不足部分用空格填充。
描述符“:{1}”表示字段宽度由第二个参数定义。
描述符“:>10”强制字段右对齐,不足部分用空格填充。
描述符“:&>10”强制右对齐,不足部分不用空格而是用“&”字符填充。
6.7 用%格式化字符串
字符串取模操作符%由两部分组成:左侧是字符串,右侧是元组。字符串取模操作符将会扫描左侧的字符串,查找特定的格式化序列(formatting sequence),并按顺序将其替换为右侧的值而生成新的字符串。
- >>> "%s is %s of %s" % ("Today","28th","June")
- 'Today is 28th of June'
- >>>
6.7.1 使用格式化序列
- >>> "Pi is <%-6.2f>" % 3.14159
- 'Pi is <3.14 >'
- >>> "Pi is <%6.2f>" % 3.14159
- 'Pi is < 3.14>'
- >>>
- >>> "Pi is <%3.2f>" % 3.14159
- 'Pi is <3.14>'
- >>>
%-6.2f 字符总数6,小数点后2位,左对齐
6.7.2 命名参数和格式化序列
- >>> num_dict = {'e': 2.718, 'pi': 3.14159}
- >>> print("%(pi).2f - %(pi).4f - %(e).2f" % num_dict)
- 3.14 - 3.1416 - 2.72
- >>>
用print函数控制输出
控制分隔符和每行的结束符
- >>> print("a","b","c",sep=" | ")
- a | b | c
- >>>
- >>> print("a","b","c",end="\n\n")
- a b c
-
- >>>
print函数还可以将结果输出到文件
- >>> print("a","b","c",file=open("testfile.txt","w"))
- >>>
-
- # cat testfile.txt
- a b c
7.2 字典的其他操作
keys方法获取字典中的所有键
values方法获取到存储在字典中的所有值
items方法将所有键及其关联值以元组序列的形式返回
- >>> english_to_french = {'red': 'rouge', 'blue': 'bleu', 'green': 'vert'}
- >>> list(english_to_french.keys())
- ['red', 'blue', 'green']
- >>> list(english_to_french.values())
- ['rouge', 'bleu', 'vert']
- >>> list(english_to_french.items())
- [('red', 'rouge'), ('blue', 'bleu'), ('green', 'vert')]
- >>>
del语句可用于移除字典中的条目,即键值对
- >>> del english_to_french['green']
- >>> english_to_french
- {'red': 'rouge', 'blue': 'bleu'}
- >>>
keys、values和items方法的返回结果都不是列表,而是视图(view)。
- >>> english_to_french.items()
- dict_items([('red', 'rouge'), ('blue', 'bleu')])
- >>>
如果要访问的键在字典中不存在,则会被Python视为出错。这时可以用in关键字先检测一下字典中是否存在该键。
或者还可以用get函数进行检测。
- >>> english_to_french.get('blue', 'No translation')
- 'bleu'
- >>> english_to_french.get('chartreuse', 'No translation')
- 'No translation'
- >>> english_to_french
- {'red': 'rouge', 'blue': 'bleu', 'green': 'vert'}
- >>>
get和setdefault方法的区别在于,以上setdefault调用完毕后,会在字典中生成键'chartreuse'和对应的值'No translation'。
- >>> english_to_french.setdefault('chartreuse', 'No translation')
- 'No translation'
- >>> english_to_french
- {'red': 'rouge', 'blue': 'bleu', 'green': 'vert', 'chartreuse': 'No translation'}
- >>>
copy方法会生成字典的浅副本,大多数情况下应该能满足需要了。如果字典值中包含了可修改对象,如列表或其他字典,那就可能需要用到copy.deepcopy函数生成深副本。
- >>> x = {0: 'zero', 1: 'one'}
- >>> y = x.copy()
- >>> y
- {0: 'zero', 1: 'one'}
- >>>
字典的update方法会用第二个字典(即参数)的所有键/值对更新第一个字典(即调用者)。
- >>> x
- {0: 'zero', 1: 'one'}
- >>>
- >>> z = {1: 'ONE', 2: 'TWO'}
- >>> x.update(z)
- >>> x
- {0: 'zero', 1: 'ONE', 2: 'TWO'}
- >>>
7.3 单词计数
- >>> sample_string = "To be or not to be"
- >>> occurrences = {}
- >>> for word in sample_string.split():
- ... occurrences[word] = occurrences.get(word,0) + 1
- ...
- >>> occurrences
- {'To': 1, 'be': 2, 'or': 1, 'not': 1, 'to': 1}
- >>>
以上已被标准化为Counter类,内置于collections模块中
- >>> from collections import Counter
- >>> sample_string = "To be or not to be"
- >>> count_result = Counter(sample_string.split())
- >>> print(count_result)
- Counter({'be': 2, 'To': 1, 'or': 1, 'not': 1, 'to': 1})
- >>>
7.4 可用作字典键的对象
上面的例子用了字符串作为字典键。但是不仅是字符串,任何不可变(immutable)且可散列(hashable)的Python对象,都可被用作字典的键。
对于元组,只有不包含任何可变嵌套对象的元组才是可散列的,可有效地用作字典的键。