二、 列表和元祖
2.1序列
python内置了多种序列,常用的有列表和元祖,还有一种是字符串。列表和元祖的差别在于列表是可修改,而元祖不可以。
2.2通用的序列操作
2.2.1索引
>>>greeting='hello'
>>>greeting[0]
>>>greeting[-1] #表示倒数第一个
'h'
'o'
2.2.2切片
使用切片来访问特定范围内的元素,为此可以使用两个索引,并用冒号分隔,遵循左闭右开
number[1,2,3,4,5,6,7]
number[3:6] [4,5,6]
number[0:1] [1]
执行切片操作时,如果第一个索引位于第二个索引的后面,结果为空序列。如果切片结束于序列末尾,可省略第二个索引,同理,如果索引起始于开头,省略第一个索引
>>> number[-3:]
[8,9,10]
>>> number[:3]
[1,2,3]
修改步长,步长默认是1,步长不能为0,但可以为负数,表示从右向左提取元素
>>> number[0:10:2]
[1,3,5,7,9]
>>> number[::-2]
[10,8,6,4,2]
2.2.3序列相加
序列相加即为拼接
>>> [1,2,3]+[4,5,6]
[1,2,3,4,5,6]
2.2.4乘法
>>> 'python'*3
'pythonpythonpython'
>>> [42]*3
[42,42,42]
None、空列表和初始化
>>> sequence=[None]*10 #创建长度为10的空列表
>>> number=[0]*10 #初始化一个全为0的长度为10的列表
2.2.5成员资格
检查特定值是否包含在序列中,使用in
,返回布尔值true
或者false
>>> permissions='rw' #早期python只支持成员是否在序列中,现在in可用于检查一个字符串是否是另一个的字串
>>> 'w' in permissions
True
长度、最小值和最大值
函数len返回序列包含元素个数,而min和max反别返回序列中最小和最大的元素
>>> numbers=[100,34,679]
>>> len(numbers)
3
>>> max(numbers)
679
2.3列表
函数list
鉴于不能像修改列表那样修改字符串,因此有些情况下使用字符串来创建列表很有帮助
>>> list('hello')
['h','e','l','l','o']
修改列表
>>>x=[1,1,1]
>>>x[1]=2 #修改列表值
>>>del x[2] #删除元素
切片赋值
>>> x[1:]=list('123') #切片赋值
>>> number=[1,5]
>>> number[1:1]=[2,3,4] #插入新元素
[1,2,3,4,5]
>>> numbers[1:4]=[] # 删除元素
列表方法
#append 末尾附加元素
>>> lst=[1,2,3]
>>> lst.append(4)
>>> lst
[1,2,3,4]
#clear 清空列表
>>> lst=[1,2,3]
>>> lst.clear()
[]
#copy 相当于深拷贝
>>>a=[1,2,3]
>>>b=a # 相当于浅拷贝,b只是a的别名
>>>b=a.copy() #a[:] 或 list(a)是一样的效果
# extend 用一个列表扩展另一个
>>> a=[1,2,3]
>>> b=[4,5,6]
>>> a.extend(b)
>>> a
[1,2,3,4,5,6]
# count 元素出现次数
>>>['to','be','or','not','to','be'].count('to')
>2
# index 查找指定值第一次出现的索引,不存在引发异常
>>> knights=['we','are','the','knights','who','say','ni']
>>> knights.index('who')
4
# insert 插入元素
>>> numbers=[1,2,3,4,5]
>>> numbers.insert(3,'four')
[1,2,3,'four',4,5]
# 可以使用切片获取同样效果
number[3:3]=['four']
#pop 删除列表最后一个元素,并返回该元素
>>> x=[1,2,3]
>>> x.pop()
3
# remove 用于删除第一个为指定值的元素
>>> x=[1,2,3,4,5]
>>> x.remove(2)
>>> x
[1,3,4,5]
# reverse 相反顺序排列元素,修改列表,但不返回任何值
>>> x=[1,2,3]
>>> x.reverse()
>>> x
[3,2,1]
#sort 将列表排序,修改列表,不返回值
>>> x=[1,3,2,5,4]
>>> x.sort()
>>> x
[1,2,3,4,5]
#sorted 函数,返回排序后的副本
>>> x=[1,3,2,5,4]
>>> y=sorted(x) #x列表不改变
>>> y
[1,2,3,4,5]
# 高级排序
"""方法sort接受两个可选参数:key和reverst.这两个参数通常是按名称指定的,称为关键词参数。
参数key类似于参数cmp:你将其设置为一个用于排序的函数。然而,不会直接使用这个函数判断元素大小,
而是使用它为每个元素创建一个键,再根据这些对元素进行排序"""
>>> x=['aaardvark','abalone','acme','add']
>>>> x.sort(key=len)
>>> x
['add','acme','abalone','aaardvark']
# 另一个关键字参数reverse,只需将其指定为一个真值,则按相反的顺序排列
# sorted函数也接受这两个参数
2.4元组
元组的语法很简单,只要将一些值用逗号分隔,就会自动创建一个元组
>>> 1,2,3
(1,2,3)
#空元祖用不包含任何内容的括号表示
>>> ()
()
#只包含一个值得元组
>>> (42,)
(42,)
#函数tuple与list相似,将序列转成元组
>>> tuple([1,2,3])
(1,2,3)
3.字符串
元素赋值和切片赋值对字符串来说是非法的
3.1设置字符串格式
转换说明符
>>> format="hello,%s.%s enough for ya"
>>> values=('world','hot')
>>> format % values
'hello,world.hot enough for ya'
- 字段名:索引或标识符,指出要设置哪个值的格式并使用结果来替换该字段。除指定值
外,还可指定值的特定部分,如列表的元素。 - 转换标识:跟在叹号后面的单个字符。当前支持的字符包括r(表示repr)、s(表示str) 和a(表示ascii)。如果你指定了转换标志,将不使用对象本身的格式设置机制,而是使用指定的函数将对象转换为字符串,再做进一步的格式设置。
- 格式说明符::跟在冒号后面的表达式(这种表达式是使用微型格式指定语言表示的)。格式说明符让我们能够详细地指定最终的格式,包括格式类型(如字符串、浮点数或十六进制数),字段宽度和数的精度,如何显示符号和千位分隔符,以及各种对齐和填充方式。
字符串方法format
# 每个字段使用花括号括起来,替换字段没有名称或者使用索引
>>> "{},{} and {}".format('first','second','third')
'first,second and third'
>>> "{3} {0} {2} {1} {3} {0}".format('be','not','or','to')
'to be or not to be'
>>> from math import pi
>>> "{name} is approximate {value:.2f}".format(value=pi,name="π")
'π is approximately 3.14'
# 格式说明符.2f,使用冒号将其与字段名分隔开
# 转换标识符用法
>>> print("{pi!s} {pi!r} {pi!a}".format(pi="π"))
π 'π' '\u03c0'
# 格式说明符用法
>>> "the number is{num:f}".format(num=42)
'the number is 42.000000'
宽度、精度和千分位分隔符
# 浮点数默认显示小数点后六位
>>> "{num:10}".format(num=3)
' 3'
>>>"{name:10}".format(name="bob")
'bob '
# 字符串和数字对齐方式不同
符号、对齐和用0填充
有很多用于设置数字格式的机制,比如便于打印整齐的表格。在大多数情况下,只需指定宽
度和精度,但包含负数后,原本漂亮的输出可能不再漂亮。另外,正如你已看到的,字符串和数
的默认对齐方式不同。在一栏中同时包含字符串和数时,你可能想修改默认对齐方式。在指定宽
度和精度的数前面,可添加一个标志。这个标志可以是零、加号、减号或空格,其中零表示使用
0来填充数字。
>>> '{:010.2f}'.format(pi)
'0000003.14'
要指定左对齐、右对齐和居中,可分别使用<、>和^。
>>> print('{0:<10.2f}\n{0:^10.2f}\n{0:>10.2f}'.format(pi))
3.14
3.14
3.14
可以使用填充字符来扩充对齐说明符,这样将使用指定的字符而不是默认的空格来填充。
>>> "{:$^15}".format(" WIN BIG ")
'$$$ WIN BIG $$$'
还有更具体的说明符=,它指定将填充字符放在符号和数字之间
>>> print('{0:10.2f}\n{1:10.2f}'.format(pi, -pi))
3.14
-3.14
>>> print('{0:10.2f}\n{1:=10.2f}'.format(pi, -pi))
3.14
- 3.14
如果要给正数加上符号,可使用说明符+(将其放在对齐说明符后面),而不是默认的-。如
果将符号说明符指定为空格,会在正数前面加上空格而不是+。
>>> print('{0:-.2}\n{1:-.2}'.format(pi, -pi)) #默认设置
3.1
-3.1
>>> print('{0:+.2}\n{1:+.2}'.format(pi, -pi))
+3.1
-3.1
>>> print('{0: .2}\n{1: .2}'.format(pi, -pi))
3.1
-3.1
3.3字符串方法
# center 通过在两边添加填充字符(默认为空格)让字符串居中,返回副本
>>> "The Middle by Jimmy Eat World".center(39)
' The Middle by Jimmy Eat World '
>>> "The Middle by Jimmy Eat World".center(39, "*")
'*****The Middle by Jimmy Eat World*****'
# find 在字符串中查找子串。如果找到,就返回子串的第一个字符的索引,否则返回-1。
>>> 'With a moo-moo here, and a moo-moo there'.find('moo')
7
>>> subject = '$$$ Get rich now!!! $$$'
>>> subject.find('!!!', 0, 16) # 同时指定了起点和终点
-1
# join 其作用与split相反,用于合并序列的元素。
>>> seq = [1, 2, 3, 4, 5]
>>> sep = '+'
>>> sep.join(seq) # 尝试合并一个数字列表
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: sequence item 0: expected string, int found
>>> seq = ['1', '2', '3', '4', '5']
>>> sep.join(seq) # 合并一个字符串列表
'1+2+3+4+5'
#lower 方法lower返回字符串的小写版本
>>> 'Trondheim Hammer Dance'.lower()
'trondheim hammer dance'
# replace 将指定子串都替换为另一个字符串,并返回替换后的结果
>>> 'This is a test'.replace('is', 'eez')
'Theez eez a test'
# split 其作用与join相反,用于将字符串拆分为序列。默认在单个或多个连续空白字符(空格、制表符、换行符)处拆分
>>> '1+2+3+4+5'.split('+')
['1', '2', '3', '4', '5']
>>> '/usr/bin/env'.split('/')
['', 'usr', 'bin', 'env']
>>> 'Using the default'.split()
['Using', 'the', 'default']
# translate 然而,使用translate前必须创建一个转换表。这个转换表指出了不同Unicode码点之间的转
# 换关系。要创建转换表,可对字符串类型str调用方法maketrans,这个方法接受两个参数:两个
#长度相同的字符串,它们指定要将第一个字符串中的每个字符都替换为第二个字符串中的相应字符
>>> table = str.maketrans('cs', 'kz')
>>> table
{115: 122, 99: 107}
>>> 'this is an incredible test'.translate(table)
'thiz iz an inkredible tezt'
3.4 判断字符串是否满足特定的条件
很多字符串方法都以is打头,如isspace、isdigit和isupper,它们判断字符串是否具有特定
的性质(如包含的字符全为空白、数字或大写)。如果字符串具备特定的性质,这些方法就返回
True,否则返回False。
4字典
4.1字典创建和使用
字典由键及其相应的值组成,这种键值对称为项(item)。在每个键与其值之间都用冒号(:)分隔,项之间用逗号分隔,而整个字典放在花括号内。空字典(没有任何项)用两个花括号表示,类似于下面这样:{}。
phonebook = {'Alice': '2341', 'Beth': '9102', 'Cecil': '3258'}
#可以使用函数dict从其他映射或键值对序创建字典
>>> items = [('name', 'Gumby'), ('age', 42)]
>>> d = dict(items)
>>> d
{'age': 42, 'name': 'Gumby'}
>>> d['name']
'Gumby'
# 还可使用关键字实参来调用这个函数
>>> d = dict(name='Gumby', age=42)
>>> d
{'age': 42, 'name': 'Gumby'}
4.1.1 字典基本操作
字典的基本行为在很多方面都类似于序列。
len(d)
返回字典d包含的项(键-值对)数。d[k]
返回与键k相关联的值。d[k]
= v将值v关联到键k。del d[k]
删除键为k的项。k in d
检查字典d是否包含键为k的项。
虽然字典和列表有多个相同之处,但也有一些重要的不同之处。
- 键的类型:字典中的键可以是整数,但并非必须是整数。字典中的键可以是任何不可变
的类型,如浮点数(实数)、字符串或元组。 - 自动添加:即便是字典中原本没有的键,也可以给它赋值,这将在字典中创建一个新项。
然而,如果不使用append或其他类似的方法,就不能给列表中没有的元素赋值。 - 成员资格:表达式k in d(其中d是一个字典)查找的是键而不是值,而表达式v in l(其
中l是一个列表)查找的是值而不是索引。这看似不太一致,但你习惯后就会觉得相当自
然。毕竟如果字典包含指定的键,检查相应的值就很容易
4.1.2 将字符串格式设置功能用于字典
在有些情况下,通过在字典中存储一系列命名的值,可让格式设置更容易些。例如,可在字典中包含各种信息,这样只需在格式字符串中提取所需的信息即可。为此,必须使用format_map来指出你将通过一个映射来提供所需的信息。
>>> phonebook
{'Beth': '9102', 'Alice': '2341', 'Cecil': '3258'}
>>> "Cecil's phone number is {Cecil}.".format_map(phonebook)
"Cecil's phone number is 3258."
4.1.3字典方法
clear: 删除所有的字典项,什么都不返回(即返回None)
>>> d = {}
>>> d['name'] = 'Gumby'
>>> d['age'] = 42
>>> d
{'age': 42, 'name': 'Gumby'}
>>> returned_value = d.clear()
>>> d
{}
>>> print(returned_value)
None
>>> x = {}
>>> y = x
>>> x['key'] = 'value'
>>> y
{'key': 'value'}
>>> x.clear()
>>> y
{
copy:返回一个新字典,其包含的键-值对与原来的字典相同(这个方法执行的是浅复制,因为值本身是原件,而非副本)。
# 当替换副本中的值时,原件不受影响。然而,如果修改副本中的值(就地修改而不是替换),原件也将发生变化,因为原件指向的也是被修改的值
>>> x = {'username': 'admin', 'machines': ['foo', 'bar', 'baz']}
>>> y = x.copy()
>>> y['username'] = 'mlh'
>>> y['machines'].remove('bar')
>>> y
{'username': 'mlh', 'machines': ['foo', 'baz']}
>>> x
{'username': 'admin', 'machines': ['foo', 'baz']}
# 使用用模块copy中的函数deepcopy
>>> from copy import deepcopy
>>> d = {}
>>> d['names'] = ['Alfred', 'Bertrand']
>>> c = d.copy()
>>> dc = deepcopy(d)
>>> d['names'].append('Clive')
>>> c
{'names': ['Alfred', 'Bertrand', 'Clive']}
>>> dc
{'names': ['Alfred', 'Bertrand']}
fromkeys:返回新建的一个字典,其中包含指定的键,且每个键对应的值都是None
>>> {}.fromkeys(['name', 'age'])
{'age': None, 'name': None}
# dict是所有字典所属的类型,fromkeys可以指定默认值
>>> dict.fromkeys(['name', 'age'], '(unknown)')
{'age': '(unknown)', 'name': '(unknown)'}
get:方法get为访问字典项提供了宽松的环境。通常,如果你试图访问字典中没有的项,将引发
错误。
>>> d = {}
>>> print(d['name'])
Traceback (most recent call last):
File "<stdin>", line 1, in ?
KeyError: 'name'
# 而使用get不会这样:
>>> print(d.get('name'))
None
#可以设置默认值
>>> d.get('name', 'N/A')
'N/A'
items:返回一个包含所有字典项的列表,其中每个元素都为(key, value)的形式。字典项在列表中的排列顺序不确定。返回值属于一种名为字典视图的特殊类型。字典视图可用于迭代,另外,你还可确定其长度以及对其执行成员资格检查。
>>> d = {'title': 'Python Web Site', 'url': 'http://www.python.org', 'spam': 0}
>>> d.items()
dict_items([('url', 'http://www.python.org'), ('spam', 0), ('title', 'Python Web Site')])
>>> it = d.items()
>>> len(it)
3
>>> ('spam', 0) in it
True
keys:方法keys返回一个字典视图,其中包含指定字典中的键。
pop:可用于获取与指定键相关联的值,并将该键-值对从字典中删除。
>>> d = {'x': 1, 'y': 2}
>>> d.pop('x')
1
>>> d
{'y': 2}
popitem:方法popitem类似于list.pop,但list.pop弹出列表中的最后一个元素,而popitem随机地弹出一个字典项,因为字典项的顺序是不确定的,没有“最后一个元素”的概念。如果你要以高效地方式逐个删除并处理所有字典项,这可能很有用,因为这样无需先获取键列表。
>>> d = {'url': 'http://www.python.org', 'spam': 0, 'title': 'Python Web Site'}
>>> d.popitem()
('url', 'http://www.python.org')
>>> d
{'spam': 0, 'title': 'Python Web Site'}
setdefault:方法setdefault有点像get,因为它也获取与指定键相关联的值,但除此之外,setdefault还在字典不包含指定的键时,在字典中添加指定的键-值对。
>>> d = {}
>>> d.setdefault('name', 'N/A')
'N/A'
>>> d
{'name': 'N/A'}
>>> d['name'] = 'Gumby'
>>> d.setdefault('name', 'N/A')
'Gumby'
>>> d
{'name': 'Gumby'}
# 如果指定的键存在,就返回其值,并保持字典不变。与get一样,值是可选的;如果没有指定,默认为None。
>>> d = {}
>>> print(d.setdefault('name'))
None
>>> d
{'name': None}
values:返回一个由字典中的值组成的字典视图。不同于方法keys,方法values返回的视图可能包含重复的值
>>> d = {}
>>> d[1] = 1
>>> d[2] = 2
>>> d[3] = 3
>>> d[4] = 1
>>> d.values()
dict_values([1, 2, 3, 1])
5条件、循环及其他语句
5.1print与import
# 打印多个参数
>>> print('Age:', 42)
Age: 42
# 自定义分隔符
# 它将逗号和变量greeting相加。如果需要,可自定义分隔符:
>>> print("I", "wish", "to", "register", "a", "complaint", sep="_")
I_wish_to_register_a_complaint
# 模块导入
import somemodule
from somemodule import somefunction
from somemodule import somefunction, anotherfunction, yetanotherfunction
from somemodule import *
# 利用as使用别名
>>> import math as foobar
>>> foobar.sqrt(4)
2.0
>>> from math import sqrt as foobar
>>> foobar(4)
2.0
5.2赋值魔法
# 多个变量赋值
>>> x, y, z = 1, 2, 3
>>> print(x, y, z)
1 2 3
序列解包
>>> values = 1, 2, 3
>>> values
(1, 2, 3)
>>> x, y, z = values
>>> x
1
#这在使用返回元组(或其他序列或可迭代对象)的函数或方法时很有用。假设要从字典中随
#便获取(或删除)一个键-值对,可使用方法popitem,它随便获取一个键-值对并以元组的方式
#返回。接下来,可直接将返回的元组解包到两个变量中。
>>> scoundrel = {'name': 'Robin', 'girlfriend': 'Marion'}
>>> key, value = scoundrel.popitem()
>>> key
'girlfriend'
>>> value
'Marion'
# 可使用星号运算符(*)来收集多余的值,这样无需确保值和变量的个数相同
>>> a, b, *rest = [1, 2, 3, 4]
>>> rest
[3, 4]
# 还可将带星号的变量放在其他位置。
>>> name = "Albus Percival Wulfric Brian Dumbledore"
>>> first, *middle, last = name.split()
>>> middle
['Percival', 'Wulfric', 'Brian']
# 赋值语句的右边可以是任何类型的序列,但带星号的变量最终包含的总是一个列表。在变量和值的个数相同时亦如此。
>>> a, *b, c = "abc"
>>> a, b, c
('a', ['b'], 'c')
链式赋值
链式赋值是一种快捷方式,用于将多个变量关联到同一个值。这有点像前一节介绍的并行赋
值,但只涉及一个值:
x = y = somefunction()
#等价写法
y = somefunction()
x = y
#不一定等价
x = somefunction()
y = somefunction()
增强赋值
# 增强赋值可用于其他数据类型(只要使用的双目运算符可用于这些数据类型)
>>> fnord = 'foo'
>>> fnord += 'bar'
>>> fnord *= 2
>>> fnord
'foobarfoobar'
5.3代码块
在Python中,使用冒号(:)指出接下来是一个代码块,并将该代码块中的每行代码都缩进相同的程度。发现缩进量与之前相同时,你就知道当前代码块到此结束了。
5.4条件和条件语句
用作布尔表达式(如用作if语句中的条件)时,下面的值都将被解释器视为假:
False None 0 “” () [] {}
if子句
name = input('What is your name? ')
if name.endswith('Gumby'):
print('Hello, Mr. Gumby')
else子句
name = input('What is your name?')
if name.endswith('Gumby'):
print('Hello, Mr. Gumby')
else:
print('Hello, stranger')
# python中的三目运算符
status = "friend" if name.endswith("Gumby") else "stranger"
elif子句
num = int(input('Enter a number: '))
if num > 0:
print('The number is positive')
elif num < 0:
print('The number is negative')
else:
print('The number is zero')
比较运算符
表达式 | 描述 |
---|---|
x == y | x 等于y |
x < y | x小于y |
x > y | x大于 y |
x >= y | x大于或等于y |
x <= y | x小于或等于y |
x != y | x不等于y |
x is y | x和y是同一个对象 |
x is not y | x和y是不同的对象 |
x in y | x是容器(如序列)y的成员 |
x not in y | x不是容器(如序列)y的成员 |
x and y | 与 |
x or y | 或 |
断言
让程序在错误条件出现时立即崩溃胜,基本上,你可要求某些条件得到满足(如核实函数参数满足要求或为初始测试和调试提供帮助),为此可在语句中使用关键字assert
>>> age = 10
>>> assert 0 < age < 100
>>> age = -1
>>> assert 0 < age < 100
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AssertionError
>>> age = -1
>>> assert 0 < age < 100, 'The age must be realistic'
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AssertionError: The age must be realistic
5.5循环
5.5.1while循环
name = ''
while not name:
name = input('Please enter your name: ')
print('Hello, {}!'.format(name))
5.5.2for循环
for number in range(1,101): #range可以设置步长
print(number)
# 迭代字典
d = {'x': 1, 'y': 2, 'z': 3}
for key in d:
print(key, 'corresponds to', d[key])
# 如果只对值感兴趣,可使用d.values。d.items以元组的方式返回键-值对。
# for循环的优点之一是,可在其中使用序列解包。
for key, value in d.items():
print(key, 'corresponds to', value)
5.5.3迭代工具
并行迭代
names = ['anne', 'beth', 'george', 'damon']
ages = [12, 45, 32, 102]
for i in range(len(names)):
print(names[i], 'is', ages[i], 'years old')
# 一个很有用的并行迭代工具是内置函数zip,它将两个
# 序列“缝合”起来,并返回一个由元组组成的序列。返回值是一个适合迭代的对象,要查看其内
# 容,可使用list将其转换为列表。
>>> list(zip(names, ages))
[('anne', 12), ('beth', 45), ('george', 32), ('damon', 102)]
for name, age in zip(names, ages):
print(name, 'is', age, 'years old')
迭代时获得索引
for string in strings:
if 'xxx' in string:
index = strings.index(string) # 在字符串列表中查找字符串
strings[index] = '[censored]'
# enumerate这个函数让你能够迭代索引值对,其中的索引是自动提供的
for index, string in enumerate(strings):
if 'xxx' in string:
strings[index] = '[censored]'
反向迭代和排序后迭代
>>> sorted([4, 3, 6, 8, 3])
[3, 3, 4, 6, 8]
>>> sorted('Hello, world!')
[' ', '!', ',', 'H', 'd', 'e', 'l', 'l', 'l', 'o', 'o', 'r', 'w']
>>> list(reversed('Hello, world!'))
['!', 'd', 'l', 'r', 'o', 'w', ' ', ',', 'o', 'l', 'l', 'e', 'H']
>>> ''.join(reversed('Hello, world!'))
'!dlrow ,olleH'
5.5.4跳出循环
#break 跳出循环
from math import sqrt
for n in range(99, 0, -1):
root = sqrt(n)
if root == int(root):
print(n)
break
#continue 跳到下一次迭代开头
时很有用。在这种情况下,可使用continue,如下所示:
for x in seq:
if condition1: continue
if condition2: continue
if condition3: continue
do_something()
do_something_else()
do_another_thing()
etc()
while True/break成例
while True:
word = input('Please enter a word: ')
if not word: break
# 使用这个单词做些事情:
print('The word was ', word)
5.6简单推导
列表推导是一种从其他列表创建列表的方式,类似于数学中的集合推导。列表推导的工作原
理非常简单,有点类似于for循环。
>>> [x * x for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> [x*x for x in range(10) if x % 3 == 0]
[0, 9, 36, 81]
# 还可添加更多的for部分
>>> [(x, y) for x in range(3) for y in range(3)]
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
# 字典推导
>>> squares = {i:"{} squared is {}".format(i, i**2) for i in range(10)}
>>> squares[8]
'8 squared is 64'
5.6其它
pass:可将其用作占位符。例如,你可能编写了一条if语句并想尝试运行它,但其中缺少一个代码块
if name == 'Ralph Auldus Melish':
print('Welcome!')
elif name == 'Enid':
# 还未完成……
pass
elif name == 'Bill Gates':
print('Access Denied')
del:不仅会删除到对象的引用,还会删除名称本身。 del只删除名称x,而没有删除列表本身(值)。事实上,在Python中,根本就没有办法删除值,而且你也不需要这样做,因为对于你不再使用的值,Python解释器会立即将其删除。
>>> x = 1
>>> del x
>>> x
Traceback (most recent call last):
File "<pyshell#255>", line 1, in ?
x
NameError: name 'x' is not defined
>>> x = ["Hello", "world"]
>>> y = x
>>> y[1] = "Python"
>>> x
['Hello', 'Python']
>>> del x
>>> y
['Hello', 'Python']
exec和eval
exec:调用函数exec时只给它提供一个参数绝非好事。在大多数情况下,还应向它传递一个命名空间——用于放置变量的地方;否则代码将污染你的命名空间,即修改你的变量。
>>> from math import sqrt
>>> exec("sqrt = 1")
>>> sqrt(4)
Traceback (most recent call last):
File "<pyshell#18>", line 1, in ?
sqrt(4)
TypeError: object is not callable: 1
#正确做法
>>> from math import sqrt
>>> scope = {}
>>> exec('sqrt = 1', scope)
>>> sqrt(4)
2.0
>>> scope['sqrt']
1
eval:eval是一个类似于exec的内置函数。exec执行一系列Python语句,而eval计算用字符串表示的Python表达式的值,并返回结果(exec什么都不返回,因为它本身是条语句)。
>>> eval(input("Enter an arithmetic expression: "))
Enter an arithmetic expression: 6 + 18 * 2
42
# 与exec一样,也可向eval提供一个命名空间,虽然表达式通常不会像语句那样给变量重新赋值
#向exec或eval提供命名空间时,可在使用这个命名空间前在其中添加一些值。
>>> scope = {}
>>> scope['x'] = 2
>>> scope['y'] = 3
>>> eval('x * y', scope)
6
6抽象
6.1函数定义
def hello(name):
return 'Hello, ' + name + '!'
def fibs(num):
result = [0, 1]
for i in range(num-2):
result.append(result[-2] + result[-1])
return result
6.1.1函数文档
要给函数编写文档,以确保其他人能够理解,可添加注释(以#打头的内容)。还有另一种编写注释的方式,就是添加独立的字符串。在有些地方,如def语句后面(以及模块和类的开头,这将在第7章和第10章详细介绍),添加这样的字符串很有用。放在函数开头的字符串称为文档字符串(docstring),将作为函数的一部分存储起来。下面的代码演示了如何给函数添加文档字符串:
def square(x):
'Calculates the square of the number x.'
return x * x
# 特殊的内置函数help很有用。可使用它获取有关函数的信息,其中包含函数的文档字符串。
>>> help(square)
Help on function square in module __main__:
square(x)
Calculates the square of the number x.
6.1.2参数
字符串(以及数和元组)是不可变的(immutable),这意味着你不能修改它们(即只能替换
为新值)。因此这些类型作为参数没什么可说的。但如果参数为可变的数据结构(如列表)呢
>>> def try_to_change(n):
... n = 'Mr. Gumby'
...
>>> name = 'Mrs. Entity'
>>> try_to_change(name)
>>> name
'Mrs. Entity'
>>> def change(n):
... n[0] = 'Mr. Gumby'
...
>>> names = ['Mrs. Entity', 'Mrs. Thing']
>>> change(names)
>>> names
['Mr. Gumby', 'Mrs. Thing']
#使用切片创造副本
>>> change(names[:])
>>> names
['Mrs. Entity', 'Mrs. Thing']
在有些语言(如C++、Pascal和Ada)中,经常需要给参数赋值并让这种修改影响函数外部的变量。在Python中,没法直接这样做,只能修改参数对象本身。但如果参数是不可变的(如数)呢?不好意思,没办法。在这种情况下,应从函数返回所有需要的值(如果需要返回多个值,就以元组的方式返回它们)。例如,可以像下面这样编写将变量的值加1的函数:
>>> def inc(x): return x + 1
...
>>> foo = 10
>>> foo = inc(foo)
>>> foo
11
#如果一定要修改参数,可玩点花样,比如将值放在列表中,如下所示:
>>> def inc(x): x[0] = x[0] + 1
...
>>> foo = [10]
>>> inc(foo)
>>> foo
[11]
关键字参数和默认值
def hello_3(greeting='Hello', name='world'):
print('{}, {}!'.format(greeting, name))
>>> hello_3()
Hello, world!
>>> hello_3('Greetings')
Greetings, world!
>>> hello_3('Greetings', 'universe')
Greetings, universe!
收集参数
参数前面的星号将提供的所有值都放在一个元组中,赋值时带星号的变量收集多余的值。它收集的是列表而不是元组中多余的值
def print_params(*params):
print(params)
>>> print_params(1, 2, 3)
(1, 2, 3)
def print_params_2(title, *params):
print(title)
print(params)
>>> print_params_2('Nothing:')
Nothing:
()
与赋值时一样,带星号的参数也可放在其他位置(而不是最后),但不同的是,在这种情况下你需要做些额外的工作:使用名称来指定后续参数。星号不会收集关键字参数。要收集关键字参数,可使用两个星号。
>>> def in_the_middle(x, *y, z):
... print(x, y, z)
...
>>> in_the_middle(1, 2, 3, 4, 5, z=7)
1 (2, 3, 4, 5) 7
>>> in_the_middle(1, 2, 3, 4, 5, 7)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: in_the_middle() missing 1 required keyword-only argument: 'z'
# 星号不会收集关键字参数
>>> print_params_2('Hmm...', something=42)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: print_params_2() got an unexpected keyword argument 'something'
# 要收集关键字参数,可使用两个星号。这样得到的是一个字典而不是元组
>>> def print_params_3(**params):
... print(params)
>>> print_params_3(x=1, y=2, z=3)
{'z': 3, 'x': 1, 'y': 2}
# 组合使用
def print_params_4(x, y, z=3, *pospar, **keypar):
print(x, y, z)
print(pospar)
print(keypar)
>>> print_params_4(1, 2, 3, 5, 6, 7, foo=1, bar=2)
1 2 3
(5, 6, 7)
{'foo': 1, 'bar': 2}
>>> print_params_4(1, 2)
1 2 3
()
{}
分配参数
前面介绍了如何将参数收集到元组和字典中,但用同样的两个运算符(和*)也可执行相
反的操作。与收集参数相反的操作是什么呢?假设有如下函数:
def add(x, y):
return x + y
同时假设还有一个元组,其中包含两个你要相加的数params = (1, 2)
这与前面执行的操作差不多是相反的:不是收集参数,而是分配参数。这是通过在调用函数(而不是定义函数)时使用运算符*实现的。
>>> add(*params)
3
这种做法也可用于参数列表的一部分,条件是这部分位于参数列表末尾。通过使用运算符**,
可将字典中的值分配给关键字参数。如果你像前面那样定义了函数hello_3,就可像下面这样做:
>>> params = {'name': 'Sir Robin', 'greeting': 'Well met'}
>>> hello_3(**params)
Well met, Sir Robin!
如果在定义和调用函数时都使用*或**,将只传递元组或字典。因此还不如不使用它们,还
可省却些麻烦。
>>> def with_stars(**kwds):
... print(kwds['name'], 'is', kwds['age'], 'years old')
...
>>> def without_stars(kwds):
... print(kwds['name'], 'is', kwds['age'], 'years old')
...
>>> args = {'name': 'Mr. Gumby', 'age': 42}
>>> with_stars(**args)
Mr. Gumby is 42 years old
>>> without_stars(args)
Mr. Gumby is 42 years old
6.2作用域/命名空间
>>> def foo(): x = 42
...
>>> x = 1
>>> foo()
>>> x
1
在这里,函数foo修改(重新关联)了变量x,但当你最终查看时,它根本没变。这是因为调用foo时创建了一个新的命名空间,供foo中的代码块使用。赋值语句x = 42是在这个内部作用域(局部命名空间)中执行的,不影响外部(全局)作用域内的x。在函数内使用的变量称为局部变量(与之相对的是全局变量)。参数类似于局部变量,因此参数与全局变量同名不会有任何问题。
如果要在函数中访问全局变量,如果只是想读取这种变量的值(不重新关联它),通常不会有任何问题
>>> def combine(parameter): print(parameter + external)
...
>>> external = 'berry'
>>> combine('Shrub')
Shrubberry
如果有一个局部变量或参数与你要访问的全局变量同名,就无法直接访问全局变量,因为它被局部变量遮 住了。如果需要,可使用函数globals来访问全局变量。这个函数类似于vars,返回一个包含全局变量的字典。(locals返回一个包含局部变量的字典)
>>> def combine(parameter):
... print(parameter + globals()['parameter'])
...
>>> parameter = 'berry'
>>> combine('Shrub')
Shrubberry
重新关联全局变量(使其指向新值)是另一码事。在函数内部给变量赋值时,该变量默认为
局部变量,除非你明确地告诉Python它是全局变量。那么如何将这一点告知Python呢?
>>> x = 1
>>> def change_global():
... global x
... x = x + 1
...
>>> change_global()
>>> x
2
Python函数可以嵌套,即可将一个函数放在另一个函数内,如下所示:、
def foo():
def bar():
print("Hello, world!")
bar()
嵌套通常用处不大,但有一个很突出的用途:使用一个函数来创建另一个函数。这意味着可像下面这样编写函数:
def multiplier(factor):
def multiplyByFactor(number):
return number * factor
return multiplyByFactor
在这里,一个函数位于另一个函数中,且外面的函数返回里面的函数。也就是返回一个函数,而不是调用它。重要的是,返回的函数能够访问其定义所在的作用域。换而言之,它携带着自己所在的环境(和相关的局部变量)!每当外部函数被调用时,都将重新定义内部的函数,而变量factor的值也可能不同。由于Python的嵌套作用域,可在内部函数中访问这个来自外部局部作用域(multiplier)的变量
>>> double = multiplier(2)
>>> double(5)
10
>>> triple = multiplier(3)
>>> triple(3)
9
>>> multiplier(5)(4)
20
像multiplyByFactor这样存储其所在作用域的函数称为闭包。通常,不能给外部作用域内的变量赋值,但如果一定要这样做,可使用关键字nonlocal。这个关键字的用法与global很像,让你能够给外部作用域(非全局作用域)内的变量赋值。
6.3递归
- 基线条件(针对最小的问题):满足这种条件时函数将直接返回一个值。
- 递归条件:包含一个或多个调用,这些调用旨在解决问题的一部分。
以阶乘为例
def power(x, n):
if n == 0:
return 1
else:
return x * power(x, n - 1)
二分查找
def search(sequence, number, lower, upper):
if lower == upper:
assert number == sequence[upper]
return upper
else:
middle = (lower + upper) // 2
if number > sequence[middle]:
return search(sequence, number, middle + 1, upper)
else:
return search(sequence, number, lower, middle)
函 数 | 描 述 |
---|---|
map(func, seq[, seq, …]) | 对序列中的所有元素执行函数 |
filter(func, seq) | 返回一个列表,其中包含对其执行函数时结果为真的所有元素 |
reduce(func, seq[, initial]) | 等价于 func(func(func(seq[0], seq[1]), seq[2]), …) |
sum(seq) | 返回 seq 中所有元素的和 |
apply(func[, args[, kwargs]]) | 调用函数(还提供要传递给函数的参数) |