2018-01-16 Python学习第四天

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�?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342