用for循环打印斐波那契数(一个数列,每个数是前两个数之和)
>>>fibs=[0,1]
>>>for i in range(10):
>>> fibs.append(fibs[-2]+fibs[-1])
#这里i的值没有用到,i被打印的次数就是循环的次数
>>>fibs
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
动态处理范围
>>>fibs=[0,1]
>>>num=int(input('请输入范围:'))
>>>for i in range(num):
>>> fibs.append(fibs[-2]+fibs[-1])
>>>print(fibs)
请输入范围:6
[0, 1, 1, 2, 3, 5, 8, 13]
如果把上述程序抽象化,创建一个fibs函数,则只需要传参并调用函数
>>>num=int(input('请输入范围:'))
>>>print(fibs(num))
fibs函数:
>>>def fibs(num):
>>> result=[0,1]
>>> for i in range(num):
>>> result.append(result[-2]+result[-1])
>>> return result
用简单的Python程序描述动作,然而这些操作的具体细节将在其他地方(独立的函数定义)中给出
>>>page = download_page()
>>>freqs = compute_frequencies(page)
>>>for word, freq in freqs:
>>> print(word, freq)
函数可以执行特定的操作并根据实际情况返回值,有些函数需要传入参数后才能调用,判断某个对象是否可调用,使用内置函数callable()
>>> import math
>>> x = 1
>>> y = math.sqrt
>>> callable(x)
False
>>> callable(y)
True
函数用def 定义
def hello(name):
return 'hello:'+name
return 用于返回函数值
>>>def hello(name):#hello是函数名 name是参数名,name是形参
>>> print('hello:'+name)#这里编写的是对参数进行的操作,可以直接执行print
>>> #return 'hello:'+name#也可以先用return返回结果,在调用的时候打印调用函数
>>>hello('jessica')
#传入参数,‘jessica’是实参 调用hello函数,由于hello函数已经包含print动作了,所以直接返回字符串
>>>#print(hello('jessica'))
#return只是返回一个对象,没有把动作写死,需要在调用的时候执行打印动作或者其他动作
hello:Jessica
>>>list(hello('jessica'))
#也可以执行其他动作,例如列表化这个对象
['h', 'e', 'l', 'l', 'o', ':', 'j', 'e', 's', 's', 'i', 'c', 'a']
用#注释,或者在函数开头添加字符串,这种字符串称为文档字符串
>>>def square(x):
>>> '这个函数用于求一个数的平方'
>>> return x*x
查看该文档字符串的方法,调用__doc__属性
>>>square.__doc__
'这个函数用于求一个数的平方'
内置函数help()在交互式解释器中,可以用开获取有关函数的信息,包括文档字符串
>>>help(square)
Help on function square in module __main__:
square(x)
这个函数用于求一个数的平方
打印出的信息与shift+tab键查看API的内容是一样的
数学意义上的函数总是返回根据参数计算得到的结果,但是这里的函数有些什么都不返回,什么都不返回的没有return,或者return后面没有指定值
>>>def test():
>>> print('one')
>>> return
>>> print('two')
>>>x=test()
one
这里的return只是结束函数,相当于break,因此不会运行到print(‘two’)
>>> x
>>>
>>> print(x)
None
因此,所有的函数其实都有返回值,如果没有指定,都返回None
确保函数在参数正确时正确完成任务,并且在参数不对的时候以显而易见的方式失败(断言或者异常)
函数内部的参数改变不会影响外部参数,参数存储在作用域
当参数类型是字符串、数和元组时,内部变量也没有改变
>>>def change(n):
>>> n='a'
>>> print(n)
>>>name='b'
>>>change(name)
a
>>>name
‘b’
过程如下:
name=’b’
n=name
n=’a’#替换
name
‘b’
字符串(以及数和元组)是不可变的(immutable),这意味着你不能修改它们(即只能替为新值)
但是参数类型是可变的数据结构,如列表,那么,内部变量是可以变的
>>>def change(n):
>>> n[0]='a'
>>> print(n)
>>>name=['b','c']
>>>change(name)
['a', 'c']
>>>name
['a', 'c']
过程如下:
name =[‘b’,‘c’]
n=name
n[0]=’a’#修改
name
[‘a’, ‘c’]
如果要避免修改原值的情况,那么传入的参数就不能是原值name,而应该是副本name[:]
>>>def change(n):
>>> n[0]='a'
>>> print(n)
>>>name=['b','c']
>>>change(name[:])
['a', 'c']
>>>name#这里的原值没有变化
['b', 'c']
Notice:name==name[:],name is not name[:]
函数的局部名称和全局名称不会起冲突,上述name都可替换成n,且不会和局部的n冲突
CASE基础知识
Notice:字典的多重切片
>>>d={'s1':{'name':['jack','allen'],
>>> 'profession':['doctor','driver']},
>>> 's2':{'age':[35,41],
>>> 'phone':[86825,87702]}
>>> }
>>>d['s1']
{'name': ['jack', 'allen'], 'profession': ['doctor', 'driver']}
>>>d['s1']['name']
['jack', 'allen']
Notice:字典的自动添加功能
>>>a={}
>>>a['age']=[1,2]
>>>a
{'age': [1, 2]}
Notice:在一个字符串外部加[],使之成为一个list类型的数据,与直接用list()不同
>>>me='王大妮'
>>>type(me)
str
>>>[me]
['王大妮']
>>>type([me])
list
>>>list(me)
['王', '大', '妮']
Notice:setdefault(x,[])的性质
>>>s={'王': ['王大妮']}
>>>s.setdefault('王',[]) #当x存在的时候,setdefault(x,值)会返回原字典对应值(值可以是空值,空值可以是[] () ‘’ ,甚至连逗号都没有)
['王大妮']
>>>s.setdefault('二',[]) #当x不存在的时候setdefault(x,[])会返回后面的[](也可以是()‘’或者任何,它将会原样返回,同时新的键值对 x:[] 也被添加到字典中去了)
[]
CASE:编写一个程序,储存姓名,并根据第一个字,中间字,最后字查找
普通方法
#步骤一:初始化一个容器,使得容器中有三个key,这里是字典storage
>>>storage = {}
>>>storage['first'] = {}
>>>storage['middle'] = {}
>>>storage['last'] = {}
>>>storage
{'first': {}, 'middle': {}, 'last': {}}
#步骤二:存储数据
#第一次将包含姓名列表直接赋值到字典storage的子字典的key和value
>>>me='王大妮'
>>>storage['first']['王']=[me]
>>>storage['middle']['大']=[me]
>>>storage['last']['妮']=[me]
>>>storage
{'first': {'王': ['王大妮']}, 'middle': {'大': ['王大妮']}, 'last': {'妮': ['王大妮']}}
#第二次是修改(append)字典storage的子字典的value,实现列表value增加元素,或者增storage字典的新key和value
>>>sister='王二妮'
>>>storage['first'].setdefault('王',[]).append(sister)
>>>storage['middle'].setdefault('二',[]).append(sister)
>>>storage['last'].setdefault('妮',[]).append(sister)
>>>storage
{'first': {'王': ['王大妮', '王二妮']},
'middle': {'大': ['王大妮'], '二': ['王二妮']},
'last': {'妮': ['王大妮', '王二妮']}}
#步骤三:使用切片查找
>>>storage['first']['王']
['王大妮', '王二妮']
建立函数方法
a初始化容器的函数
>>>def init(container): #需要传入一个容器
>>> container[‘first’]={} #用赋值方法在容器中添加‘first’:[]键值对,这个容器是个映射(字典)
>>> container[‘middle’]={}
>>> container[‘last’]={}
b存储数据的函数
>>>def store(con,full_name): #把需要存储的数据full_name存放到一个容器con
>>> names=full_name.split() #把字符串full_name打散,用列表names装
#优化问题1:split()方法适用于用空格分隔字符串的西方名字,连一起写的中文名如何处理
#优化问题2:如果一个人的名字只有两个name字
>>> for label,name in zip (['first','middle','last'],names): #zip()缝合两个列表,返回元组压缩包
>>> people=con[label].get(name) #get方法只会返回对应的name对应的值
----------------*get--append*方式--------------------------------------------------
>>> if people: #如果people已经有值了,该条件为true
>>> people.append(full_name) #那么在列表中追加新的姓名
>>> else:
>>> con[label][name]=[full_name] #否则people还是空的话,初始赋值
----------------*setdefault*方式--------------------------------------------------------------
>>> data[label].setdefault(name,[]).append(full_name)
#用setdefault方法就不需要if
#如果还没有任何name:值,则填入name:[],并在[]追加full_name
#如果已经存在[值],则直接追加full_name
c查询函数
>>>def lookup(cont,lab,nam):
>>> return cont[lab][nam]
传入实际参数并调用函数
>>>data={}
>>>init(data)
>>>store(data,’王 大 妮’)
>>>lookup(data,’first’,’王‘)
多次储存和查询只需要重复调用对应函数
优化问题:
优化1对于中文字符没有空格或者其他特殊符号间隔的,用list(),即names=list(full_name)
优化2只有两个字的姓名,在当前函数下,第二个子会默认作为‘middle’,如果想把第二个字作为‘last’,只要加一个条件判断语句,在两字姓名中间添加一个空元素,使得middle与之匹配
if len(names)==2:names.insert(1,’’)
Python中,没有办法通过给参数赋值来修改外部的变量
>>>def change(x):
>>> x=5
>>> return x
>>>a=4
>>>change(a)
5
>>>a
4 #a不会被函数改变
只能通过变量自身的重新赋值
>>>def change(x):
>>> x=5
>>> return x
>>>a=4
>>>a=change(a)
>>>a
5
或者将值放在列表中
>>>def change(x):
>>> x[0]=5
>>>a=[4]
>>>change(a)
>>>a
[5]
调用函数时的传参顺序,默认会和函数的名称顺序相对应
>>>def hello(b, a):
>>> print('{}, {}!'.format(a, b))
>>>hello('hello','world')
world, hello!
这里,默认会将hello默认赋值给b,world指定赋值给a
但是,实际参数较多的情况下,这种错误会很容易出现,因此,可以在调用的时候指定参数的名称
>>>def hello(b, a):
>>> print('{}, {}!'.format(a, b))
>>>hello(a='hello',b='world')
hello, world!
这种使用名称指定的参数成为关键字参数,有助于澄清各个参数的作用
此外在编写函数时使用关键字,可以指定默认值,当调用函数但没有传参,或者部分参数未传时返回默认值
>>>def hello(b='python',a='hi'): #函数定义的时候就设定默认值
>>> print('{},{}!'.format(a,b))
>>>hello() #没有传参就使用在函数中已经设置的默认值
hi,python!
>>>def hello(b='python',a='hi'):
>>> print('{},{}!'.format(a,b))
>>>hello('world')
hi, world! #传参就覆盖默认值
>>>def hello(b='python',a='hi'):
>>> print('{},{}!'.format(a,b))
>>>hello(a='hello')
hello,python!
如果不是所有参数都设置默认值,那么没有设置默认值的参数是必传参数
>>>def hello(b,a='hi'):
>>> print('{},{}!'.format(a,b))
>>>hello()
报错,一定需要传入参数b
>>>def hello(b,a='hi'):
>>> print('{},{}!'.format(a,b))
>>>hello('beijing')
hi,beijing!
如果有多个参数,默认值参数要排在最后,放在中间会报错
>>>def hello(b,c,a='hi'):
print('{},{}!{}'.format(a,b,c))
hello(b='beijing',c='aa')
hi,beijing!aa
参数前面加*可以收集剩余的值,带*参数放中间,则后面的参数需要用名称来指定
>>>def print_params(title,*params,end):
>>> print(title,params,end)
>>>print_params('age:',45,25,14,end='year old')
age: (45, 25,14) year old
* params也可以放在最后或者最前面,只要调用函数传参的时候关键字参数跟随在位置参数后面
>>>def print_params(*params,title,end):
>>> print(title,params,end)
>>>print_params(45,25,14,title='age:',end='year old')#关键字参数必须跟随在位置参数后面!
age: (45, 25, 14) year old
* params返回的是一个元组,如果没有可供收集的元素,那么将返回一个空元组
>>>print_params('age:',end='year old')
age: () year old
* params不能收集关键字参数,** params可以
>>>def print_params(title,**params):
>>> print(title,params)
>>>print_params('age:',a=44,b=24)
age: {'a': 44, 'b': 24}
** params得到的是一个字典,但是** params只能放在最后,因为传参的关键字参数总是在后面
>>>def print_params(title,name,sex='male',*num,**params):
>>> print(title,name,sex,num,params)
>>>print_params('age:','jack','female','s','d',a=44,b=24)
age: jack female ('s', 'd') {'a': 44, 'b': 24}
这里的设置默认值其实是没有什么效果的,如果真的不传sex的参数,会把后面’s’参数默认当做sex参数
CASE:如果让之前的store函数可以传入多个姓名,即传入的不再是一个元素,而是多个元素,可以用*params收集这些元素,形成一个元组,再以遍历的方式,将元素取出来一个个拆分、缝合、添加到data
>>>def store(con,*full_names):
>>> for full_name in full_names:
>>> names=list(full_name)
>>> for label,name in zip (['first','middle','last'],names):
>>> data[label].setdefault(name,[]).append(full_name)
收集:在定义函数的时候用*params收集剩余的参数
>>>def collect(*params):
>>> return params[0]+params[1]
>>>collect(1,2)
3
Notice:该函数可以优化
>>>def sum(a):
>>> s=0
>>> for i in a:
>>> s=s+i
>>> print(s)
>>>def collect(*params):
>>> sum(params)
>>>collect(1,2,3)
6
分配:在定义函数的时候指定确定个数和内容的参数,传的时候却用的*params传入,调用函数的时候元组内的元素分配给函数里这些参数
>>>def add(x,y):
>>> return x+y
>>>params=(1,2)
>>>add(*params)
3
同样,如果是字典,也可以用**params传入分配
>>>def hello(a,b):
>>> print('{},{}!'.format(a,b))
>>>params=('hello','world')
>>>hello(*params)
>>>params={'a':'hello','b':'world'} #收集的时候关键字参数成为字典,分配的时候传入字典分配
>>>hello(**params)
hello,world!
如果再定义函数和调用函数时都使用*或**,与都不使用的效果是一样的,只用于传递字典或元组,因此*或**在定义函数(允许可变数量的参数)或者调用函数时(拆分字典或序列)使用,才能发挥作用
使用这些拆分运算符来传递参数很有用,因为这样无需操心参数个数之类的问题
>>>def story(**kwds):
>>> return 'once upon a time,there was a ' \
>>> '{job} called {name}'.format_map(kwds)
-----------------------------------------------------------------------------
>>>story(job='driver',name='hellen')#传入关键字参数
------------------------------------------------------------------------------
>>>params={'job':'driver','name':'hellen'}
>>>story(**params)#定义和调用**等于都不用,这里将def中的**和传参括号的**同时去掉,结果是一样的
-----------------------------------------------------------------------------
>>>params={'name':'hellen'}
>>>story(job='driver',**params)#但是当字典数据不完整时,**params不能缺
'once upon a time,there was a driver called hellen'
>>>def power(x,y,*others):
>>> if others:
>>> print('reundant param:',others)
>>> return pow(x,y)
>>>power(2,3,4) # power幂函数
reundant param: (4,) #\* params返回的是一个元组
8
>>>params=(3,)*3#三个元素组成的元组
>>>power(*params)#分配
reundant param: (3,) #\* params返回的是一个元组
27
>>>def interval(start,stop=None,step=1):
>>> if stop is None:
>>> start,stop=0,start
>>> result=[]
>>> i=start
>>> while i<stop:
>>> result.append(i)
>>> i+=step
>>> return result
>>>interval(2) #stop没有传入新参数,这里相当于自动创建升序列表功能
[0, 1]
>>>interval(2,10,2)
[2, 4, 6, 8]
>>>power(*interval(2,10,2))
reundant param: (6, 8)
16
变量可视为指向值的名称,看不见的字典,vars()内置函数返回该函数
>>>x=1
>>>scope=vars()
>>>scope
{'__name__': '__main__',
'__doc__': 'Automatically created module for IPython interactive environment',
'__package__': None,
'__loader__': None,
'__spec__': None,
'__builtin__': <module 'builtins' (built-in)>,
'__builtins__': <module 'builtins' (built-in)>,
'_ih': ['',
"x=1\nscope['x']",
"x=1\nscope=vars()\nscope['x']",
'x=1\nscope=vars()\nscope'],
'_oh': {2: 1},
'_dh': ['C:\\Users\\mengxiaC\\python_test'],
'In': ['',
"x=1\nscope['x']",
"x=1\nscope=vars()\nscope['x']",
'x=1\nscope=vars()\nscope'],
'Out': {2: 1},
'get_ipython': <bound method InteractiveShell.get_ipython of <ipykernel.zmqshell.ZMQInteractiveShell object at 0x00000000047BF400>>,
'exit': <IPython.core.autocall.ZMQExitAutocall at 0x48230b8>,
'quit': <IPython.core.autocall.ZMQExitAutocall at 0x48230b8>,
'_': 1,
'__': '',
'___': '',
'_i': "x=1\nscope=vars()\nscope['x']",
'_ii': "x=1\nscope['x']",
'_iii': '',
'_i1': "x=1\nscope['x']",
'x': 1,
'_i2': "x=1\nscope=vars()\nscope['x']",
'scope': {...},
'_2': 1,
'_i3': 'x=1\nscope=vars()\nscope'}
可见scope这个字典包含的内容很多,x=1作为键值也被存储了进去,这个scope成为命名空间或者作用域,这里的scope是全局作用域。实际上每个函数调用都将创建一个局部作用域
>>>def foo():
>>> x=22
>>> sc=vars()
>>> return sc
>>>foo()
{'x': 22}
这个sc就是局部作用域,x=22就存在这个局部作用域
在函数中读取全局变量通常没有问题,
>>>a='hello'
>>>def greet(param):
>>> print(a,param) #在函数中读取全局变量
>>>greet('world')
hello world
但是如果有个局部变量同名于要访问的全局变量,通常只能访问到局部变量,全局变量被覆盖,需要使用函数globals来访问全局变量,
这个函数类似于vars,返回一个包含全局变量的字典。(locals返回一个包含局部变量的字典。)
>>>a='hello'
>>>def greet(param,a):
>>> print(globals()['a'],param,a)
>>>greet('world','wide')
hello world wide
重新关联全局变量,使其指向新值,利用global声明要改变的是全局变量
>>>x=1
>>>def change(x):
>>> x+=1
>>> print(x)
>>>change(x)
2
>>>x
1
>>>x=1
>>>def change():
>>> global x
>>> x=x+1
>>> print(x)
>>>change()
2
>>>x
2 #全局变量x已经被改变
可在内部函数中访问这个来自外部局部作用域的变量
>>>def defy (a):
>>> def defr(b):
>>> return a*b #a来自函数defr外部
>>> return defr
>>>defy(4)(5)
像defr这样存储其所在作用域的函数称为闭包
通常,不能给外部作用域内的变量赋值,但如果一定要这样做,可使用关键字nonlocal。
这个关键字的用法与global很像,让你能够给外部作用域(非全局作用域)内的变量赋值。
函数可调用其他函数,也可以调用自己。简单地说,递归意味着引用(这里是调用)自身。
>>>def recursion():
>>> return recursion()
这是一个无穷递归,会一直运行,消耗内存,程序终止时报错
有用的递归应该包含以下部分:
基线条件(针对最小的问题):满足这种条件是函数将直接返回一个值
递归条件:包含一个或多个调用,这些调用旨在解决问题的一部分
案例1:阶乘函数
>>>def mu(n):
>>> s=1
>>> for i in range(1,n+1):
>>> s*=i
>>> print(i,s)
>>> print(s)
>>>mu(4)
1 1
2 2
3 6
4 24
24=1*2*3*4
优化:
>>>def mu(n):
>>> s = n
>>> for i in range(1, n):
>>> s *= i
>>> print(i,s)
>>> return s
>>>mu(4)
1 4
2 8
3 24
24=4*1*2*3
必要条件:
1的阶乘为1。
对于大于1的数字n,其阶乘为n - 1的阶乘再乘以n。
>>>def factorial(n):
>>> if n == 1:
>>> return 1#不能省略,相当于这个函数的出口,达到这个条件后将不再调用自身
>>> else:
>>> return n * factorial(n - 1)
案例2:幂函数
>>>def power(a,n):
>>> s=1
>>> for i in range(n):
>>> s=s*a #i只用于计次,不参与运算
>>> return s
>>>power(3,4)
81
必要条件:
power(a,0)=1,一次都没循环
n>0,power(a,n)=a*power(a,n-1)
>>>def power(a,n):
>>> if n==0:
>>> return 1
>>> else:
>>> return a*power(a,(n - 1))
>>>print(power(3,4))
必要条件:
如果上限和下限相同,就说明它们都指向数字所在的位置,因此将这个数字返回。
否则,找出区间的中间位置(上限和下限的平均值),再确定数字在左半部分还是右半部分。然后在继续在数字所在的那部分中查找。
lis=[4,5,6,7,8,9] 找7
: lower upper
index: 0 5 中间值lis[middle=(0+5)//2=2]=6<7 定位在右边
2+1=3 5 中间值lis[middle (3+5)//2=4]=8>7 在右半边找
3 4 中间值lis[middle (3+4)//2=3]=7=7
lis=[4,5,6,7,8,9] 找5
: lower upper
index: 0 5 中间值lis[middle=(0+5)//2=2]=6>5
0 2 中间值lis[middle (0+2)//2=1]=5=5
函数:
>>>def search(lis,num,lower,upper):
>>> if lower==upper:
>>> assert num==lis(lower)#使用assert断言防止崩溃
>>> return lower
>>> else:
>>> middle=(lower+upper)//2
>>> if num>lis[middle]: #在右边,保留upper
>>> return search(lis,num,middle+1,upper)
#middle+1是因为middle=(lower+upper)//2
>>> else:
>>> return search(lis,num ,lower,middle)
解释:middle+1,如果不加1
假设num=7,lis=【4,5,6,7,8,9】
lis [4,5,6,7,8,9,]
[6,7,8,9]
[6,7]#此时7永远大于lis[middle]=lis[2]=6,
无论返回多少次search(lis,num,middle,upper) =search(lis,num,2,3),函数陷入死循环
Notice: 二分法查找比index()方法查找效率高,模块bisect提供了标准的二分查找实现。
函数式编程
使用map将序列的所有元素传递给函数。
list(map(str, range(10))) # 与列表推导[str(i) for i in range(10)]等价
使用filter根据布尔函数的返回值来对元素进行过滤
>>> def func(x):
... return x.isalnum() #检查是否都是字母或数值
...
>>> seq = ["foo", "x41", "?!", "***"]
>>> list(filter(func, seq))
['foo', 'x41']
等价于
>>> [x for x in seq if x.isalnum()]
['foo', 'x41']
实际上,Python提供了一种名为lambda表达式的功能,让你能够创建内嵌的简单函数(主要供map、filter和reduce使用)。
>>> filter(lambda x: x.isalnum(), seq)
['foo', 'x41']
>>> numbers = [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33]
>>> from functools import reduce
>>> reduce(lambda x, y: x + y, numbers)
1161