2.相等运算符
3.is:同一性运算符
#避免将is运算符用于比较类似数值和字符串这类不可变值,由于Python内部操作这些对象的方式的原因,使用is运算符的结果是不可预测的。
4.in: 成员资格运算符
【代码】
name = input('what
is your name?')
if 's' in name:
print('Your name contains the letter "s".')
else:
print ('Your name does not contain the letter
"s".')
【结果】
what is your name?nihao
Your name does not contain the letter "s".
5.字符串和序列比较
【代码】
print("alpha"< "beta")
print(
'FnOrD'.lower())
print(
'FnOrD'.upper())
print([
1,2] < [2,1]) #参与比较的不是字符而是元素的其他类型print([2,[1,4]] <[2,[1,5]]) #如果一个序列中包含其他序列元素,比较规则同样适用于序列元素
【结果】
True
fnord
FNORD
True
True
6.布尔运算符
【代码】
#有时想要检查一个以上的条件,可以这样做:number = int(input('Enter
a number between 1 and 10:'))
if number <= 10:
if number
>=1:
print('Great!')
else:
print('Wrong!')
else:
print('Wrong')
#下面使用布尔思想进行优化if number <= 10 and number >= 1:
print('Great')
else:
print('Wrong')
#and运算符就是所谓的布尔运算符。它连接两个布尔值,并且在两者都为真时返回真,否则返回假。与它同类的还有两个运算符,or和not。
#
布尔运算符有个有趣的特性:只有在需要求值时才进行求值。例如,表达式x and y需要两个变量都为真时才为真,如果x为假,表达之会立刻返回false,不管y的值。这类短路逻辑可以用来实现C和Java中所谓的三元运算符。
【结果】
Enter a number between 1 and 10:111
Wrong
Wrong
5.4.7 断言
【代码】
#if语句有个非常有用的“近亲”,它的工作方式像下面:
#if not condition
# crash program
#
就是因为其他程序在晚些时候崩溃,比如在错误条件出现时直接让它崩溃。语句中使用的关键字是assertage = 10
assert 0 <
age < 100
age = -1
assert 0 <
age < 100
【结果】
Traceback (most recent call last):
File"D:/python_file/20180113/test.py", line 8, in
assert 0 < age < 100
AssertionError
【代码】
#如果需要确保程序中的某个条件一定为真才让程序正常工作的话,assert语句就有用了,它可以在程序中置入检查点,条件后添加字符串,用于解释断言age = -1
assert 0 <
age < 100 ,'The
age must be realistic'
【结果】
Traceback (most recent call last):
File"D:/python_file/20180113/test.py", line 3, in
assert 0 < age < 100,'The age must be realistic'
AssertionError: The age must be realistic
5.5 循环
但是怎样才能重复执行多次呢?比如,写一个每月提醒付房租的程序,但是不使用循环,需要这样写:
发邮件
等一个月
发邮件
等一个月
(继续下去……)
但是如果想让程序继续执行,直到认为停止它呢?
5.5.1 while循环
【代码】
x = 1
while x <= 100:
print(x)
x+=
1
【结果】
不写了吧
【代码】
name = ''
while not name.strip(): #这回,连空格也逃脱不掉了
name = input('Please
enter your name:')
print('Hello,%s!'% name)
【结果】
不写了吧
5.5.2 for循环
【代码】
#while语句非常灵活,它可以用来在任何条件为真的情况下重复执行一个代码块。但是,有时还得量体裁衣。比如要为一个集合(序列或其他可迭代对象)的每个元素都执行一个代码块words = ['this','is','an','ex','parrot']
for word in words:
print(word)
#range函数的工作方式类似于分片,它包含下限,但不包含上限。
#range()
函数返回的是一个可迭代对象(类型是对象),而不是列表类型, 所以打印的时候不会打印列表。
# #list()
函数是对象迭代器,把对象转为一个列表。返回的变量类型为列表。a = range(0,10) #下限为0,上限为9a = list(a)
print(a)
#xrange函数的循环行为类似于range函数,区别在于range函数一次创建整个序列而xrange一次只创建一个数。当需要迭代一个巨大的序列时,xrange会更高效,一般情况下不需要关注它。
【结果】
this
is
an
ex
parrot
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
5.5.3 循环遍历字典元素
【代码】
#一个简单的for语句就能循环字典的所有键,就像处理序列一样d = {'x':1,'y':2,'z':3}
for key in d:
print(key,'correspends to',d[key])
#for循环的一大好处就是可以使用序列解包
#
字典严肃的顺序通常是没有意义的。迭代的时候,字典中的键和值都能保证被处理,但是处理顺序不确定。如果处理顺序很重要的话,可以把键保存在列表中。
【结果】
y correspends to 2
z correspends to 3
x correspends to 1
5.5.4 一些迭代工具
在Python中迭代序列时,一些函数非常有用。有些函数位于itertools模块中,还有一些内建函数也十分有用。
[if !supportLists]1. [endif]并行迭代
【代码】
#程序可以同时迭代两个序列。比如:names = ['anne','beth','george','damon']
ages = [
12,45,32,102]
#如果想要打印名字和对应的年龄,可以像下面这样做:for i in range(len(names)):
print(names[i],'is',ages[i],'years
old')
print('---------------------------------------------')
z =
zip(names,ages) #python3中,使用zip后,得到的是一个可迭代对象print(z)
for i in z:
print(i) #可以看出,打印出来的是一个元组print('---------------------------------------------')
#注:要重新获得迭代对象,不能再使用上面一个z了,以为上面的z已经迭代到末尾了z = zip(names,ages) #python3中,使用zip后,得到的是一个可迭代对象for i in z: #这里不嫩解包了?
print(i[0],'is',i[1],'years old')
【结果】
anne is 12 years old
beth is 45 years old
george is 32 years old
damon is 102 years old
---------------------------------------------
('anne', 12)
('beth', 45)
('george', 32)
('damon', 102)
---------------------------------------------
anne is 12 years old
beth is 45 years old
george is 32 years old
damon is 102 years old
【代码】
#zip函数也可以用于任意多的序列。关于它很重要的一点就是zip可以应付不等长序列:当最短的序列用完时停止z = zip(range(5),range(100000))
for i in z:
print(i)
#print(i[0],i[1])
【结果】
(0, 0)
(1, 1)
(2, 2)
(3, 3)
(4, 4)
[if !supportLists]2. [endif]编号迭代
【代码】
#有时候想要迭代序列中的对象,还想要获取当前对象的索引。例如,在一个字符串列表中替换所有包含'xxx'的子字符串。strings = ['hello,world!,xxx,yyy,zzz','dkdskldsklkl']
#strings 村庄
#string
人家
#xxx
要找的人for string in strings:#大框
if 'xxx' instring: #小框
index = strings.index(string) #index相当于门牌号
strings[index] = '[censored]'
print(strings)
【结果】
['[censored]', 'dkdskldsklkl']
【同上,代码】
#没问题,但是在替换之前搜索给定的字符串似乎没必要。如果不替换的话,搜索还会返回错误的索引,较好的版本如下index = 0
for string in strings:
if 'xxx' in string:
strings[index] =
'[censored]'
index += 1
print(strings)
【结果】
['[censored]', 'dkdskldsklkl']
【同上,代码】
#方法有点笨,不过可以接受。另一种方法是使用內建的enumerate函数:for index,string in enumerate(strings): #这样的话,一户人家和一户人家的门牌号都有了
if 'xxx' instring:
strings[index] =
'[censored]'
print(strings)
【结果】
['[censored]', 'dkdskldsklkl']
[if !supportLists]3. [endif]翻转和排序迭代
【代码】
s = sorted([4,3,6,8,3])
print(s)
s2 =
sorted('Hello,
world!')
print(s2)
s3 =
list(reversed('Hello,
world!'))
print(s3)
#reversed函数和sorted函数作用相反,但是reversed函数还需要使用list函数把得到的对象解包,sorted函数自带解包功能。
【结果】
[3, 3, 4, 6, 8]
[' ', '!', ',', 'H', 'd', 'e', 'l', 'l', 'l', 'o', 'o', 'r', 'w']
['!', 'd', 'l', 'r', 'o', 'w', ' ', ',', 'o', 'l', 'l', 'e', 'H']
5.5.5 跳出循环
一般来说,循环会一直执行直到条件为假,或者到序列元素用完时。但是有些时候可能会提前中断一个循环,进行新的迭代。
[if !supportLists]1. [endif]break
【代码】
#结束(跳出)循环可以使用break语句。假设需要寻找100以内的最大平方数,那么程序可以从100往下迭代到0,当找到一个平方数时就不需要继续循环了。from math import sqrt
for n in range(99,0,-1): #range增加了第三个参数:步长
root = sqrt(n)
if root
== int(root):
print(n)
break
【结果】
81
[if !supportLists]2. [endif]continue
for x in seq:
if condition1:continue
if condition2:continue
if condition3:continue
do_something()
do_something_else()
do_another_thing()
etc()
很多时候,只要使用if语句就可以了;
for x in seq:
if not (condition1 orcondition2 or condition3):
do_something()
do_something_else()
do_another_thing()
etc()
尽管continue语句非常有用,它却不是最本质的。应该习惯使用break语句,因为在while True语句中会经常用到它。
[if !supportLists]3. [endif]while True/break习语
【代码1】
#Python中while和for循环非常灵活,但一旦使用while语句就会遇到一个需要更多功能的问题。word = 'dummy' #代码有些丑,在进入循环体之前需要给word赋一个哑值。使用哑值就是工作没有尽善尽美的标志while word:
word =
input('please enter a word:')
#处理word
print('The word was',word)
【代码2】
#解决哑值问题while True:
word =
input('please enter a word:')
if not word: break
#处理word
print('The word was',word)
#while True部分实现了一个永远不会自己停止的循环。但在循环内部的if语句中加入条件是可以的,在条件满足时调用break语句,这样就可以在循环体内部的任何地方结束循环,而不仅仅在凯循环的开头部分。if/break语句自然地将循环分为两部分:第1部分负责初始化,第2部分则在循环条件为真的情况下使用第1部分内部初始化好的数据。
5.5.6 循环中的else子句
broke_out = False
for x in seq:
do_something(x)
if condition(x):
broke_out = True
break
do_something_else(x)
if not broke_out:
print(“I didn’t break out!”)
更简单的方式是在循环中增加一个else子句—它仅在没有调用break时执行。
【代码】
from math import sqrt
for n in range(99,81,-1):
root = sqrt(n)
if root
== int(root):
print(n)
break
else: #for循环中的所有语句都执行过的时候(没有跳出循环),从这个地方开始执行
print("Didn't find it!")
#跳出for循环的话,从这个地方开始执行
#
注:for和while循环中都可以使用continue、break语句和else子句
【结果】
Didn't find it!
5.6 列表推导式----轻量级循环
【代码】
#列表推导式是利用其他列表创建新列表(类似于数学术语中的集合推导式)的一种方法。它的工作方式类似于for循环x = [x*x for x in range(10)]
x2 = [x*x
for x in range(10) if x % 3 == 0]
xy = [(x,y)
for x in range(3) for y in range(3)] #两个for之间不需要加and
#
作为对比,下面的代码使用两个for语句创建了相同的列表:result = []
for x in range(3):
for y in range(3):
result.append((x,y))
print(x)
print(x2)
print(xy)
print(result)
#也可以和if子句联合使用,像以前一样:girls = ['alice','bernice','clarice']
boys = [
'chris','arnold','bob']
print([b + '+' + g for g in girls for b in boys if b[0] == g[0]])
#注:使用普通的圆括号而不是方括号不会得到“元组推导式”。,在最近的版本中,则会得到一个生成器。
【结果】
2
[0, 9, 36, 81]
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
['arnold+alice', 'bob+bernice', 'chris+clarice']
使用字典进行效率更高的优化,只使用一层for循环:
【代码】
girls = ['alice','bernice','clarice']
boys = [
'chris','arnold','bob']
letteGirls = {}
for girl in girls:
letteGirls.setdefault(girl[
0],[]).append(girl)
a = [b +
'+' + g for b in boys for g in letteGirls[b[0]]] #列表推导式不能使用元组"()",而应使用列表"[]"print(a)
【结果】
['chris+clarice', 'arnold+alice', 'bob+bernice']
5.7 三人行
5.7.1 什么都没发生
有时,程序什么事情都不做,这种情况,应让pass出马
【代码】
#Python中空代码是非法的,解决的方法就是在语句块中加上一个pass语句:name = 'Bill Gates'
if name == 'Ralph Auldus Melish':
print('Welcome')
elif name == 'Enid':
#还没完。。。
pass
elifname == 'Bill Gates':
print('Access Denied')
#注释pass语句联合的替代方案时插入字符串
【结果】
Access Denied
5.7.2 使用del删除
【代码】
#一般来说,Python会删除那些不再使用的对象scoundrel = {'age':42,'first
name':'Robin','last
name':'of Locksley'}
robin = scoundrel
print(scoundrel)
print(robin)
scoundrel =
None
print(robin)
#首先,robin和scoundrel都被绑定到同一个字典上。所以当设置scoundrel为None的时候,字典通过robin还是可用的。但是当把robin也设置为None的时候,字典就“漂”在内存里面了,没有任何名字绑定到它上面。所以python解释器直接删除了那个字典(这种行为被称为垃圾收集)。也可以使用None之外的其他值,字典同样会“消失不见”
【结果】
{'age': 42, 'last name': 'of Locksley', 'first name': 'Robin'}
{'age': 42, 'last name': 'of Locksley', 'first name': 'Robin'}
{'age': 42, 'last name': 'of Locksley', 'first name': 'Robin'}
【代码】
#使用del语句,不仅会移动一个对象的引用,也会移除那个名字本身
# x = 1
# del x
# print(x)
x = ['Hello','world']
y = x
y[
1] = 'Pythoin'
print(x)
del x
print(y)
#原因是删除的只是名称,而不是列表本身。事实上,在Python中是没有办法删除值的(也不需要过多考虑删除值的问题,因为在某个值不再使用的时候,Python解释器会负责内存的回收)
【结果】
['Hello', 'Pythoin']
['Hello', 'Pythoin']
5.7.3 使用exec和eval执行和求值字符串
有时可能会需要动态创造Python代码,然后将其作为语句执行或作为表达式计算,---在此之前,一定要谨慎
[if !supportLists]1. [endif]exec
【代码】
#在Python3中,exec是一个函数而不是语句exec('print("hello,world")') #exec函数的形参是一个字符串,所以两边加引号from math import sqrt
exec("sqrt
= 1")
print(sqrt(4))
#为什么一开始要这样做?exec函数最有用的地方在于可以动态创建代码字符串。为了安全起见,可以增加一个字典,起到命名空间的作用。命名空间又称为作用域,是个非常重要的只知识。
【结果】
hello,world
Traceback (most recent call last):
File"D:/python_file/20180113/test.py", line 6, in
print(sqrt(4))
TypeError: 'int' object is not callable
【示例】
#可以通过增加in 来实现,其中的就是起到放置代码字符串命名空间作用的字典。from math import sqrt
scope = {}
exec('sqrt =
1',scope) #python3中使用这种形式限定作用域print(sqrt)
print(len(scope))
print(scope['sqrt']) #可以看出,sqrt=1 中,sqrt是键,1是值print(scope.keys()) #因为內建的__builtins__字典自动包含所有的內建函数和值
【结果】
2
1
dict_keys(['sqrt', '__builtins__'])
[if !supportLists]2. [endif]eval
【示例】
#eval(用于“求值”)是类似于exec的內建函数。exec语句会执行一些列Python语句,而eval会计算Python表达式(以字符串形式书写),并且返回结果值,(exec语句并不返回任何对象,因为它本身就是语句)。e = eval(input("Enter
an arithmetic expression:"))
print(e)
#注:Python2中,表达式eval(raw_input(...))事实上等同于input(...).在Python3.0中,raw_input被重命名为input
#
可以给eval语句提供两个命名空间,一个全局的一个局部的。全局的必须是字典,局部的可以是任何形式的映射。
#
目前Python内没有任何执行不可信任代码的安全方式,一个可选的方案是使用Pythono的实现,比如Jython,以及使用一些本地机制,比如Java的sandbox
【结果】
Enter an arithmetic expression:6 + 18 * 2
42
【示例】
#初探作用域
#
给exec或者eval语句提供命名空间时,还可以在真正使用命名空间前放置一些值进去scope = {}
scope[
'x'] = 2
scope['y'] = 3
print(eval('x*y',scope))
#同理,exec或者eval调用的作用域也能在另外一个上面使用:scope = {}
exec('x = 2',scope)
print(eval('x*x',scope))
【结果】
6
4
第六章 抽 象
这里,将会学习语句组织成函数,这样,可以告诉计算机如何做事。有了函数之后,不必反反复复项计算机传递同样的具体指令了。本章还会详细介绍参数和作用域的概念。以及递归概念及其在程序中的用途。
6.1 懒惰即美德
【代码】
fibs = [0,1]
for i in range(8):
fibs.append(fibs[-
2] +fibs[-1])
print(fibs)
#如果想要一次计算前10个数的话,没有问题。甚至可以将用户输入的数字作为动态范围的长度使用,从而改变for语句循环的次数fibs = [0,1]
num =
int(input('How
many Fibonacci number do you want?')) #使用for i in range(num-2):
print('i=',i,)
fibs.append(fibs[-
2] +fibs[-1]) #主要在于循环几次(总共需要5个,原始序列中有两个)print(fibs)
【结果】
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
How many Fibonacci number do you want?5
i= 0
i= 1
i= 2
[0, 1, 1, 2, 3]
#真正的程序员会让自己的程序抽象一些,上面的程序可以改写为较抽象的版本:
#num = int(input('How many numbers do you want?'))
#print(fibs(num))
如果函数要用很多次的话,这么做会节省很多精力
6.2 抽象和结构
抽象可以节省很多工作,实际上它的作用还要更大,它是计算机程序可以让人读懂的关键(这也是最基本的要求)。计算机非常乐于处理精确和具体的指令,但是人可就不同了。
计算机会:向前走10米,左转90度,再走5步,右转45度,走123步
人只需要知道:一直沿着街道走,过桥,电影院就在左手边,这样就明白了吧!关键大家都知道怎样走路和过桥,不需要指令来知道这些事。
组织计算机程序也是类似的。程序应该是非常抽象的。事实上,我们把这段描述翻译为Python程序。
page = download_page()
freqs = compute_frequencies(page)
for word,freq in freqs:
print word,freq
6.3 创建函数
函数可以调用(可能包含参数),它执行某种行为并且返回一个值。一般来说,內建的callable函数可以用来判断函数是否可调用:
【示例】
import math
x =
1
y = math.sqrt
print(callable(x))
print(callable(y))
【结果】
False
True
【示例】
#那么,怎样定义函数呢def hello(name):
return 'Hello,'+name+'!'
print(hello('world'))
print(hello('Gumby'))
【结果】
Hello,world!
Hello,Gumby!
【示例】
def fibs(num):
result = [
0,1]
for i in range(num-2):
result.append(result[-
2]+result[-1])
return result
print(fibs(10))
print(fibs(15))
#注:result语句是用来从函数中返回值的
【结果】
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
6.3.1 记录函数
如果想要给函数写文档,让后面使用该函数人能理解的haul,可以加入注释(以#开头)。另外一个方式就是直接写上字符串。这类字符串在其他地方可能会非常有用。如果在函数的开头写下字符串,它就会作为函数的一部分进行存储,这称为文档字符串。
【示例】
def square(x):
'Calculates the square of the number x.'
return x*x
print(square(2))
【结果】
4
【示例】
def square(x):
'Calculates the square of the number x.'
return x*x
print(square(2))
print(square.__doc__) #这样对文档字符串进行访问,__doc__是函数属性。第7章会介绍更多关于属性的知识,双下划线表示它是个特殊属性。这类特殊和"魔法"属性在第9章中介绍
#
內建的help函数是非常有用的。在交互式解释器中使用它,可以得到关于函数,包括它的文档字符串的信息:print(help(square)) #第10章对help函数进行讨论
【结果】
4
Calculates the square of the number x.
Help on function square in module __main__:
square(x)
Calculates the square of thenumber x.
None
6.3.3 并非真正函数的函数
数学意义上的函数,总在计算其参数后返回点什么。Python的有些函数并不返回任何东西。但是Python的函数就是函数,即便它从学术上讲并不是函数。没有return语句,或者return后没有跟任何值的函数不返回值
【示例】
def test():
print('This is printed')
return
print('This is not')
x =test()
#可以看到,第2个return语句被跳过了(类似于循环中的break语句)
#
但是如果test不返回任何值,那么x又引用什么呢?print(x)
#好熟悉的None,所以所有的函数的确都返回了东西:当不需要它们返回值的时候,它们就返回None。看来,“有些函数并不是真正的函数”的说法有些不公平了。
#
不要被默认的行为所迷惑,当调用者期待一个序列的时候,就不会意外地返回None。
【结果】
This is printed
None
6.4 参数魔法
函数用起来很简单,创建起来也不复杂。但函数参数的用法就有时有些不可思议了。
6.4.1 值从哪里来
写在def语句中函数名后面的变量通常叫做函数的形式参数,而调用函数的时候提供的值是实际参数(值)。
6.4.2 能改变参数么
【示例】
#函数通过它的参数获得一系列值。那参数只是变量而已,所以它们的行为和预想的一样。在函数内为参数赋予新值不会改变外部任何变量的值:def try_to_change(n):
n = 'Mr. Gumby'
name = 'Mrs. Entity'
try_to_change(name)
print(name) #可以看出,该函数没有改变变量name的值
#
在try_to_change函数的内部,参数n获得了新值,但是它没有影响到name变量。n实际上是个完全不同的变量,具体的工作方式类似于下面:name = "Mrs. Entity"
n = name #这等于传递参数n = 'Mr. Gumby'
print(name)
#结果是显而易见的,当变量n改变的时候,变量name不变。同样,当在函数内部把参数重绑定(赋值)的时候,函数外部的变量不会受到影响。
#
注:参数存储在局部作用域内
【结果】
Mrs. Entity
Mrs. Entity
【示例】
#字符串(以及数字和元组)是不可变的,即无法修改(也就是说只能用新的值覆盖)。但是:如果将可变的数据结构如列表用作参数的时候会发生什么:def change(n): #关键看实参是什么,实参是字符串,则不能改变,实参是列表,则能改变
n[0] = 'Mr. Gumby'
names = ['Mrs. Entity','Mrs.
Thing']
change(names)
print(names)
#本例中,参数被改变了。这就是本例和前面例子中至关重要的区别。前面的例子中,局部变量被赋予了新值,但是这个例子中变量names所绑定的列表的确改变了。有些奇怪了吧?
#
下面进行模仿names = ['Mrs. Entity','Mrs.
Thing']
n = names
#再来一次,模拟传参行为n[0] = 'Mr. Gumby' #改变列表names
【结果】
['Mr. Gumby', 'Mrs. Thing']
【示例】
#这种情况在前面已经出现很多次了。当两个变量同时引用一个列表的时候,它们的确是同时引用一个列表。如果想避免出现这种情况,可以复制一个列表的副本。在序列中做切片的时候,返回的切片总是一个副本。names =['Mrs. Entity','Mrs.
Thing']
n = names[:]
#使用切片进行复制(这个相当于字典中的深复制)
#
如果现在改变n,则不会影响到namesn[0] = 'Mr. Gumby'
print(n)
print(names)
【结果】
['Mr. Gumby', 'Mrs. Thing']
['Mrs. Entity', 'Mrs. Thing']
【示例】
#这种情况在前面已经出现很多次了。当两个变量同时引用一个列表的时候,它们的确是同时引用一个列表。如果想避免出现这种情况,可以复制一个列表的副本。在序列中做切片的时候,返回的切片总是一个副本。names =['Mrs. Entity','Mrs.
Thing']
n = names[:]
#使用切片进行复制(这个相当于字典中的深复制)
#
如果现在改变n,则不会影响到namesn[0] = 'Mr. Gumby'
print(n) #n变了print(names) #names没变
#
再用change试一下def change(n): #关键看实参是什么,实参是字符串,则不能改变,实参是列表,则能改变
n[0] = 'Mr. Gumby'
change(names[:]) #使用这种方式,传递进去的是一个串,如果形参是names,则传递进去的是一个列表print(names)
【结果】
['Mr. Gumby', 'Mrs. Thing']
['Mrs. Entity', 'Mrs. Thing']
['Mrs. Entity', 'Mrs. Thing']
[if !supportLists]1. [endif]为什么我们要修改参数
使用函数改变数据结构(比如列表或字典)是将程序抽象化的好方法。假设需要编写一个存储名字并且能用名字、中间名或姓查找联系人的程序,可以使用下面的数据结构:
【示例】
storage= {}
storage[
'first'] = {}
storage[
'middle'] = {}
storage[
'last'] = {}
#storage这个数据结构的存储方式是带有3个键“first”、“middle”和“last”的字典。每个键的下面都又存储一个字典。子字典中,可以使用名字(名字、中间名或姓)作为键,插入联系人列表作为值。me = 'Magnus Lie Hetland'
storage['first']['Magnus'] = [me] #Magnus是key,me是value(说明value是一个列表,可以使用append方法进行追加元素)storage['middle']['Lie'] =[me]
storage[
'last']['Hetland'] = [me]
#每个键下面都存储了一个以人名组成的列表。本例表中,人名只有“我”。如果想得到所有注册的中间名为Lie的人,可以像下面这样做:print(storage['middle']['Lie'])
#将人名加入到列表中的步骤有些枯燥乏味,尤其是要加入很多姓名相同的人时,因为要扩展已经存储了哪些名字的列表。例如,下面加入我姐姐的名字,而且假设不知道数据库中已经存储了什么:my_sister = 'Anne Lie Hetland'
storage['first'].setdefault('Anne',[]).append(my_sister)#Anne是key,my_sister是valuestorage['middle'].setdefault('Lie',[]).append(my_sister)
storage[
'last'].setdefault('Hetland',[]).append(my_sister)
print(storage['middle']['Lie'])
【结果】
['Magnus Lie Hetland']
['Magnus Lie Hetland', 'Anne Lie Hetland']
【代码】
#如果要写个大程序来这样更新列表,那么很显然程序很快就会变得臃肿且笨拙不堪了。抽象的要点就是隐藏更新时的繁琐的细节,这个过程可以使用函数来实现。def init(data): #data本身是个字典
data['first'] = {}
data[
'middle'] = {}
data[
'last'] = {}
#使用的方法如下:storage = {}
init(storage)
#print(storage)
#可以看到,函数包办了初始化工作,让代码更易读
#
在编写存储名字的函数前,先写个获得名字的函数def lookup(data,label,name):
return data[label].get(name)
#标签(比如“middle")以及名字(比如“Lie”)可以作为参数提供给lookup函数使用,这样会获得包含全名的列表
#
注:返回的列表和存储在数据结构中的列表是相同的,所以如果列表修改了,那么也会影响数据结构def store(data,full_name):
names = full_name.split()
#得到的是一个列表['','','',...]
if len(names)
== 2:
names.insert(
1,'') #在names列表的下标为1的位置处插入
labels = 'first','middle','last'#是一个元组
for label,name inzip(labels,names):
people = lookup(data,label,name)
if people:
people.append(full_name)
else:
data[label][name] =[full_name]
#如果原先没有值,则赋值
#
(1)通过参数data和full_name进入函数,这两个参数被设置为函数在外部获得的一些值。
#
(2)通过拆分full_name,得到一个叫做names的列表
#
(3)如果names的长度为2(只有首名和末名),那么插入一个空字符串作为中间名
#
(4)将字符串“first”、“middle”和“last”作为元组存储在labels中(也可以使用列表,这里只是为了方便而去掉括号)
#
(5)使用zip函数联合标签和名字,对于每一个(label,name)对,进行以下处理:
#
①获得属于给定标签和名字的列表
#
②将full_name添加到列表中,或者插入一个需要更新的新列表MyNames = {}
init(MyNames)
store(MyNames,
'Magnus Lie
Hetland')
print(lookup(MyNames,'middle','Lie'))
#好像可以工作,再试试store(MyNames,'Robin Hood')
store(MyNames,
'Robin Locksley')
print(lookup(MyNames,'first','Robin'))
store(MyNames,
'Mr. Gumby')
print(lookup(MyNames,'middle',''))
#可看到,如果某些人的名字、中间名或姓相同,那么结果中会包含所有这些人的信息。
#
这类程序很适合进行面向对象程序设计
【结果】
['Magnus Lie Hetland']
['Robin Hood', 'Robin Locksley']
['Robin Hood', 'Robin Locksley', 'Mr. Gumby']
[if !supportLists]2. [endif]如果我的参数不可变呢
在某些语言(如C++,Pascal和Ada)中,重绑定参数并且使这些改变到函数外的变量是很平常的事情。但在Python中,这是不可能的:函数只能修改参数对象本身。但是如果你的参数不可变—比如是数字—又该怎么办呢?
不好意思,没有办法。这个时候应该从函数中返回所有需要的值(如果值多余一个的话就以元组的形式返回)。
��1���N�?