一、函数基础
函数可以计算出一个返回值。作用:最大化代码重用,最小化代码冗余,流程的分解
1、函数相关的语句和表达式
语句        例子
Calls        myfunc(‘diege','eggs',meat=lit) #使用函数
def,return,yield      def adder(a,b=1,*c):
                          return a+b+c[0]
global        changer():
                global x;x='new'
lambda        Funcs=[lambad x:x**2,lambad x:x*3]

2、编写函数
def是可执行的代码,实时执行的,Python中所有语句都是实时执行的,if,while,def可嵌套,可以出现在任何地方,但往往包含在模块文件中,
并早模块导入时运行,函数还可以通过嵌套到if语句中去实现不同的函数定义。
def创建了一个对象并将其赋值给某一个变量名。
return将一个结果对象发送给调用者。
函数是通过赋值(对象引用)传递的。

参数通过赋值传递给函数。
global声明了一个模块级的变量并被赋值。
参数,返回值以及变量并不是声明

def语句
def语句将创建一个函数对象并将其赋值给一个变量名。一般格式如下:

def <name>(arg1,age2,...,agrN):
    <statements>
     return <value>
函数主体往往都包含了一个return语句(不是必须的),如果它没有出现,那么函数将会在控制流执行完函数主体时结束,技术上,没有返回值的函数自动返回了none对象。
return可以在函数主体中的任何地方出现。它表示函数调用的结束,并将结果返回至函数调用处。

函数通过嵌套到if语句中去实现不同的函数定义的格式:
if test:
    def func():
        ...
else:
    def func()
func()    #call

3、例子1
定义
>>> def Dtest(x,y):
...     return x*y
使用(call)
>>> Dtest(3,5)
15
>>> Dtest(3.14,3)
9.42
>>> Dtest('A',4)    
'AAAA'
这里又看到Python中的多态了
Dtest函数中的表达式x*y的意义完全取决于x和y的对象类型。
类似的多态操作还包括:print,index,*操作符
>>> Dtest('A','A') #会报错,但不要尝试判断传入参数的对象类型,这样实质上破坏了函数的灵活性,把函数限制在特定的类型上。任何支持函数所预期的结果的对象都能用。(结果一词是指函数所执行的一组方法和表达式运算符)
4、例子2
寻找序列的交集
定义
>>> def Intersect(seq1,seq2):
...     res=[]
...     for x in seq1:
...             if x in seq2:
...                     res.append(x)
...     return res
调用
>>> Intersect([1,3,5,6,7,9],[2,4,5,7,8,10])
[5, 7]
>>> s1='SPAM'
>>> s2='sCaM'
>>> Intersect(s1,s2)                        
['M']
重访多态,这些编写这个函数是多态,它可以支持多种类型
>>> Intersect([1,3,5,6,7,9],(1,4))             
[1]
技术上讲,文件也可以做为该函数的第1个参数,但不能作为第2个参数。在一个文件中搜索2个参数
>>> Intersect(open('/etc/rc.conf').read(),'apache')

5、本地变量
例子2中.res变量在python中称为本地变量,这个变量只是在def内的函数中是可见的,并且仅在函数运行时是存在的。所有函数内部进行赋值的变量名
都默认为本地变量,。
*req是明显被赋值过的,所以它是一个本地变量
*参数也是通过赋值被传入的,所以seq1和seq2也是本地变量。
*for循环将元素赋值给了一个变量,所以变量x也是本地

二、作用域和参数
(一)作用域
python作用域:变量定义以及查找的地方
参数传递:传递给函数作为其输入对象的方式
1、作用域法则
Python创建,改变或者查找变量名都是在所谓的命名空间(一个保存变量名的地方)中进行。作用域这个术语指的就是命名空间。
也就说,在代码中变量名被赋值的位置决定了这个变量名能被访问到的范围
一个函数所有变量名都与函数的命名空间相关联。
*def内定义变量名def内使用
*def之中的变量名与def之外的变量名不发生冲突,使用别处相同的变量名也没问题。
x=99
def func()
    x=88
函数定义了本地作用域,而模块定义了全局作用域。两作用域关系。
*内嵌的模块是全局作用域:对于外部的全局变量就成为了一个模块对象的属性。
*全局作用域的作用范围仅限单个文件:不要被全局迷惑,这里的全局是一个文件的顶层的变量名,仅对这个文件内部的代码而言是全局。
Python中,没有一个无所不包的情景文件作用域。替代的方法是,变量名由模块文件隔开,必须精准地导入一个模块文件才能偶使用这文件中
定义的变量名,
*每次对函数的调用都创建了一个新的本地作用域。
*赋值的变量名废除声明为全局变量,否则均为本地变量。
*所用的变量名都可以归纳为本地,全局,或者内置。(内置:ptyhon预定义的__builtin__模块提供的)
2、变量名解析:LEGB原则
对一个def语句
*变量名引用分为三个作用域进行查找:首先查找本地,然后是函数内(如果有),之后全局,最后内置。
*默认情况下,变量名赋值会创建或改变本地变量
*全局声明将赋值变量名映射到模块文件内部的作用域。
3、作用域实例
#定义全局作用域
>>> X=99
>>> def func(Y):
...     Z=X+Y   #本地作用域
...     return Z
...
>>> func(1)    
100
全局变量:X,func
本地变量:Y,Z
4、内置作用域
需要导入__builtin__
>>> import  __builtin__
>>> dir(__builtin__)
里面前一半是内置异常,后一半是内置函数
5、global
global语句包含关键字global
*全局变量是位于模块文件内部顶层的变量名
*全局变量如果是在函数内部被赋值的话,并需经过声明
*全局变量名在函数的内部不经过声明也可以被引用
>>> X=88     
>>> def func():
...     global X
...     X=99
...
>>> func()
>>> print X
99
>>> y,z=1,2
>>> def Ftest():
...     global x
...     x=y+z
...
>>> Ftest()
>>> x
3
6、最小化全局变量

(二)传递参数
*参数的传递是通过自动将对象赋值给本地变量来实现的。
*在函数内部的参数名的赋值不会影响调用者。
*改变函数的可变对象参数的值也许会对调用者有影响。
换句话说,因为参数是简单的通过赋值进行对象的传递,函数能够改变传入的可变对象,因此其结果会影响调用者。
*不可变参数是“通过值”进行传递。
像整数和字符串这样的对象是通过对象引用而不是拷贝进行传递的,但是因为你无论如何都不可能在原处改变不可变对象,实际的效果就是很像创建了一份拷贝。
可变对象是通过“指针”进行传递的。
1、参数和共享引用
>>> def changer(a,b):
...     a=2
...     b[0]='diege'
...
>>> X=1
>>> L=[1,2]
>>> changer(X,L)
>>> X,L
(1, ['diege', 2])
函数内部对参数或者参数的分片赋值了,X没改变,L改变了,X,Y都是全局变量。这说明了数字字符串不可变参数函数无法改变。
而列表,字典等可改变参数可以在执行函数调用后改变。
这里a是函数的本地变量名,第一个赋值对函数调用者没有影响。它仅简单的修改了本地变量名a,并没有改变参数a绑定的函数调用者的变量名X。
b也是一个本地变量名,但是他被传递给了一个可变对象。因为第二个赋值是一个在原处发生的对象改变(如果是b='diege'就没有改变,因为这样只改变本地变量名),对函数中b[0]进行赋值的结果是在函数返回后影响L的值。事实上我们没有修改b,修改是是b当前所引用
对象的一部分,并且这个改变将影响调用者。
2、避免可变参数的修改
在Python中,默认通过引用(也就是指针)进行函数的参数传递。如果不想在函数内部在原处的修改影响传递给它的对象。那么,能够简单的创建一个可变对象的拷贝。我们总是能够在调用时对列表进行拷贝L=[1,2]
changer(X,L[:])
如果不想改变传入的对象,无论函数是如何调用的,我们可以在函数内部进行拷贝,避免可变参数的修改
>>> def changer(a,b):
...     a=2
...    b=b[:]
...     b[0]='diege'
...
3、对参数输出进行模拟
return能够返回任意种类的对象,它也能返回多个值,如这些值被封装进一个元组或者其他的集合类型
>>> def mul(x,y):
...     x=2
...     y=[3,4]
...     return x,y
...
>>> X=1
>>> L=[1,2]
>>> mul(X,L)
(2, [3, 4])
>>> X,L=mul(X,L) 
>>> X,L
(2, [3, 4])
4、特定参数匹配模型
参数在ptyhon中总是通过赋值进行传递,传入的对象赋值给了在def头部的变量名。尽管这样,在模型的上层,python提供了额外的工具,该工具改变了调用过中,赋值时参数对象匹配在头部的参数名的优先级。这些工具是可选的。
默认情况下,参数是通过其位置进行匹配的,从左到右,而且必须精确地传递和函数头部参数名一样多的参数。还能够定义变量名进行匹配,默认参数值(arg2=10),以及对于额外参数的容器,必须要根据变量名匹配对象,匹配完成后在传递机制的底层依然是赋值。这些工具对于编写库文件的人来说,要比应用程序开放者更有用。
规则
*位置:从左到右进行匹配
*关键字参数:通过参数名进行匹配。
【调用者】可以定义那个函数接受这个值,通过在调用时使用参数的变量名,使用name=value这种语法。
*默认参数:为没有传入值得参数定义参数值【定义函数时】
如果调用时传入的值过于少的话,函数能够为参数定义接受的默认值,在函数定义中使用name=value
*可变参数:收集任意多基于位置或关键字的参数
以*开头,收集任意多的额外参数
*可变参数:传递任意多的基于位置或关键字的参数。【调用时】
调用者能够再使用*语法去将参数集合打散,分成参数。这个*与函数头部的*恰恰相反。在函数头部他意味着收集任意多的参数,而在调用者中意味着传递任意多的参数。

总结与特定模式有关的语法:
语法            位置    解释
func(value)        调用者    常规参数,通过位置进行匹配
func(name=value)    调用者    关键字参数,通过变量名匹配
func(*name)        调用者    以name传递所有的对象,并作为独立的基于位置的参数
func(**name)        调用者    以name成对的传递所有的关键字/值,并作为独立的关键字的参数

def func(name)        函数    常规参数:通过位置或变量名进行匹配
def func(name=value)    函数    默认参数值:如果没有在调用中传递的话,就是用默认值
def func(*name)        函数    匹配并收集(在元组中)所有包含位置的参数
def func(**name)    函数    匹配并收集(在字典中)所有包含位置的参数。

5、关键字参数和默认参数的实例
>>> def f(a,b,c):
...     print a,b,c
...
>>> f(1,3,4)
1 3 4
关键词参数
>>> f(c=5,a=8,b=9)      
8 9 5
>>> f(4,c=8,b=9)   
4 9 8
>>> def func(name,age,job):
...     print 'name','age','job'
...
>>> func('diege',18,'op')
name age job
>>> func(age=18,job='op',name='diege')
name age job
关键字参数在调用中起到了数据标签的作用

默认参数,如果调用时没有传入值得话,在函数运行前,参数就被赋了默认值。
>>> def f(a,b=2,c=5):
...     print a,b,c
...
>>> f(4)
4 2 5
>>> def f(a,b=2,c=5):
...     print a+b+c
...
>>> f(4)
11
>>> f(6,5) 
16
>>> f(a=10)
17
>>> f(a=3,b=4)
12
>>> f(2,c=6)    
10
>>> f(6,7,8)
21
6、任意参数的实例
*和** 让函数支持接收任意数目的参数。
收集参数
第一种用法:在函数定义中,在元组中收集不匹配的位置参数
>>> def f(*args):print args
...
>>> f()
()

**特性类似,但是它只对关键字参数有效。将这些关键字参数传递给一个新的字典。
>>> def f(**agrs):print args
>>> f()
>>> def f(**args):print args
...
>>> f()
{}
min调用
def min1(*args):
    res=args[0]
    for arg in args[1:]:
        if arg < res:
            res=arg
    return
三、函数的高级话题
(一)、匿名函数:lamdba
lambad 创建了一个之后能够被调用的函数,它返回了一个函数而不是将这个函数赋值给一个变量名。
1、lambda表达式
lanbda arg1,arg2,...,argN:expression using arguments
lambda 是一个表达式,而不是一个语句
lambda的主体是一个单个的表达式,而不是代码块
>>> def func(x,y,z):return x+y+z
...
>>> func(2,3,4)
9
等同于
>>> f=lambda x,y,z:x+y+z
>>> f(2,3,4)
9
默认参数也能够再lambda参数中使用,就像在def中使用一样。
>>> x=(lambda a='free',b='file',c='feel':a+b+c)  
>>> x('well')
'wellfilefeel

为什么要使用lambda
lambda 通常用来编写跳转表,也就是行为的列表或字典,能够按照需要执行相应的动作。
L=[(lambda x:x**2),(lambda x:x**3),(lambda x:x**4)]
>>> for f in L:
...     print f(2)
...
4
8
16
>>> print L[0](3)
9

2、嵌套lambda和作用域
>>> def action(x):
...     return (lambda y:x+y)
...
>>> act=action(99)
>>> act
<function <lambda> at 0x285066f4>
>>> act(2)
101
(二) 作参数来应用函数
1、内置函数apply
当需要变得更加动态的话,可以通过将一个函数作为一个参数传递给apply来调用一个生成的函数,并且也将传给那个函数的参数作为一个元组传递给apply函数()
2、传入关键字参数
>>> echo(1,2,a=3,b=4)
(1, 2) {'a': 3, 'b': 4}

>>> arg1=(1, 2)
>>> arg2={'a': 3, 'b': 4}
>>> apply(echo,arg1,arg2)
(1, 2) {'a': 3, 'b': 4}
3、和apply类似的调用语法
>>> apply(func,args)
9
>>> func(args)
9
>>> apply(echo,arg1,arg2)
(1, 2) {'a': 3, 'b': 4}
(三) 在序列中映射函数:map
>>> L=[1,2,3,4,5]
>>> updated=[]
>>> for x in L:           
...     updated.append(x+10)
...
>>> updated
[11, 12, 13, 14, 15]
使用内置工具map,map函数会对一个序列对象中的每一个元素应用被传入的函数,并且返回一个包含了所有函数调用结果的一个列表。
>>> def inc(x):return x+10
...
>>> L=[1,2,3,4,5]
>>> map(inc,L)
[11, 12, 13, 14, 15]
map(函数,传入函数的序列对象)
>>> map(None,S1,S2)
>>> L=[1,2,3,4,5] 
map嵌套lambda      
>>> map((lambda x:x+3),L)
[4, 5, 6, 7, 8]
高级功能:提供了多个序列作为参数,它能够并行返回分别以每个序列的元素作为【函数对应参数】得到的结果的列表
>>> pow(3,4)
81
>>> map(pow,[1,2,3],[2,3,4])    #1**2,2**3,3**4
[1, 8, 81]
(四) 函数式编程工具:filter和reduce
函数式编程的意思就是对序列应用一些函数的工具。
基于某一测试函数过滤出一些元素-filter
对每对元素都应用函数并运行到最后结果-reduce
>>> range(-5,5)
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4]
>>> filter((lambda x:x>0),range(-5,5))
[1, 2, 3, 4]
这个等效于for range:if语句
reduce稍微复杂一点。这里两个reduce调用,计算在一个列表中所有元素加起来和以及乘起来的乘积
>>> reduce((lambda x,y:x+y),[1,2,3,4])
10
>>> reduce((lambda x,y:x*y),[1,2,3,4])
24

(五)重访列表解析:映射
1、列表解析基础
>>> [x**2 for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> map((lambda x:x**2),range(10))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
2、增加测试和嵌套循环
在for之后编写if分支,用来增加逻辑选择,if分支相当filter
>>> [x for x in range(5) if x%2==0]
[0, 2, 4]
>>> filter((lambda x:x%2==0),range(5))
[0, 2, 4]
filter出来的列表可以作为map的第2个参数
>>> map((lambda x:x**2),filter((lambda x:x%2==0),range(5)))
[0, 4, 16]
3、列表解析和矩阵
4、理解列表解析
对ptyhon初学者,通常使用简单的for循环,在其他大多数情况下,使用map调用(除非它们会变得过于复杂)
列表解析比map快,map比for循环快2倍

(六)重访迭代器:生成器
编写的函数能够返回一个值,并且稍后还可以从它刚才离开的地方仍然返回值。这样的函数被认作是生成器,因为它们随时间生成一个序列的值。
大多数方面生成器函数就像一般函数,在Python它们被自动用作实现迭代协议,因它只能够再迭代的语境中出现。

生成器和一般的函数之间代码上最大的不同就是一个生成器yield一直,而不是return一个值。yield语句将会将函数关起,并向它的调用者返回一个值
但是保存足够的状态信息为了让其能够在函数从它挂起的地方恢复。
包含yield的语句的函数将会特地编译成为生成器。当调用它时,他们返回一个生成器对象,这个生成器对象支持迭代器对象接口。
1、生成器实例的。
>>> def Dtest(N):
...     for i in range(N):
...             yield i**2
使用
>>> for i in Dtest(5):
...     print i,':',
...
0 : 1 : 4 : 9 : 16 :
查看过程
>>> x=Dtest(4)
>>> x.next()
0
>>> x.next()
1
>>> x.next()
4
>>> x.next()
9
>>> x.next()
for循环和生成器是一样的:通过重复调用next方法,知道捕获一个异常。
2、扩展生成器协议send和next
生成器函数协议中增加了一个send方法,send方法生成一系列结果的下一个元素,这一点就像next方法一样,但是它也提供了
一种调用者与生成器之间的进行通信的方法,从而能偶影响它的操作。
yiled是一个表达式,可以返回传入的元素来发送,而不是一个语句。值是通过调用本身send(value)方法传给生成器的。
之后恢复生成器的代码,并且yield表达式返回了为了发送而传入的值。如果调用了正常的放next()方法,yield返回None
3、迭代器和内置类型
内置的数据类型设计了对应于内置函数iter的迭代器对象。字典迭代器在每次迭代中产生关键字列表元素。
>>> D={'a':1,'b':2,'c':3}
>>> x=inter(D)
>>> x.next() 
'a'
>>> x.next()
'c'
>>> x.next()
'b'
>>> x.next()
Traceback (most recent call last):
  File "<stdin>",
所有迭代内容(包括for循环,map调用,列表解析等)一次设计用来自动调用iter函数。
通过支持迭代协议的类来实现任意的生成器对象是可能的,并且已经有很多这样的对象,在for循环和其他的迭代环境中使用。
这样的类定义了一个特别的__iter__方法,它将返回一个迭代对象。
4、生成器表达式:迭代器遇到列表解析
迭代器和列表解析的概念形成了这个语言的一个新的特性,生成器表达式。
(八)函数设计概念
*耦合性:对于输入使用参数,并且对于输出使用return语句
*耦合性:只有在真正必要的情况下使用全局变量。
*耦合性:不要改变可变类型的参数,除非调用者希望这样做。
*聚合性:每一个函数都应该有一个单一的,同一的目标
*大小:每一个函数应该相对较小。
*耦合:避免直接改变在另一个模块文件中的变量。
函数是对象:简洁调用
(九)函数陷阱
1、本地变量是静态检测的
>>> X=99
>>> def selector():
>>> def selector():
...     import __main__
...     print __main__.X
...     X=88
...     print X
...
>>> selector()
99
88
>>> X
99
所有的全局变量都再 __main__。 __main__就是顶层的命名空间.__main__.X得到了全局变量版本的X。
这里使用模块的属性标记来获得其全局变量。
2、默认和可变对象。
默认参数是在def语句运行时被评估并保存的,而不是在这个函数调用时。从内部来将,Python会将每一个默认
参数保存成一个对象。附加在这个函数本身。
3、没有return语句的函数
函数中return和yield语句是可选择。没有返回值函数自动返回None对象。
这样的函数被当做语句,就像他们只执行任务而不要计算有用的结果一样。
4、嵌套作用域的循环变量