导语:

函数是组织好,可重复使用,用来实现单一或相关联功能的代码段,能提高应用的模块性和代码的重复利用率。
Python提供了许多内建函数,比如print()。
也可以自己创建函数,被叫做用户自定义函数。

定义函数:

规则:

函数代码块以 def 关键词开头,后接函数标识符名称和圆括号()。
任何传入参数和自变量必须放在圆括号中间。圆括号之间可以用于定义参数。
函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
函数内容以冒号起始,并且缩进。
return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。

语法:

   def functionname( parameters ):
       "函数_文档字符串"
       function_suite
       return [expression]

例:
#!/usr/bin/python
def printme( str ): # 定义函数
"打印任何传入的字符串"
print str
return

调用函数

printme("我要调用用户自定义函数!")
printme("再次调用同一函数")

以上实例输出结果:
我要调用用户自定义函数!
再次调用同一函数

按引用传递参数:

所有参数在Python里都是按引用传递。如果你在函数里修改了参数,那么在调用这个函数的
函数里,原始的参数也被改变了。

例:
#!/usr/bin/python
def changeme( mylist ):
"修改传入的列表"
mylist.append([1,2,3,4]);
print "函数内取值: ", mylist
return

# 调用changeme函数
mylist = [10,20,30];
changeme( mylist );
print "函数外取值: ",mylist

传入函数的和在末尾添加新内容的对象用的是同一个引用。故输出结果如下:
函数内取值:  [10, 20, 30, [1, 2, 3, 4]]
函数外取值:  [10, 20, 30, [1, 2, 3, 4]]

文档字符串

我们在使用def 关键字定义一个函数时,其后必须跟有函数名和包括形式参数的圆括号。函数体的下一行开始,必须是缩进的。函数体的第一行可以是字符串,这个字符串就是文档字符串documentation string,通常也称作:docstring

定义:

在函数体的第一行,可以使用一对三引号(''')或者(""")来定义文档字符串,文档字符串通常第一行以大写字母开头,以句号 (.)结束,第二行是空行,第三行开始是详细描述。强烈建议为你重要的函数写文档字符串都遵循此惯例。

作用:

文档字符串是使用Python过程中一个很重要的工具,他对程序文档很有帮助,使程序很容易理解。甚至当程序运行的时候,可以从一个函数中返回文档字符串。把函数当做一个对象来看,这更有助于我们的理解,就相当于获取一个对象的属性(__doc__).

函数文档字符串举例:

#cat nester.py    这是一个模块文件
#!/usr/bin/env python3
"这是模块文档字符串"
def print_list(name):
    '''这是函数文档字符串'''
    for each_item in name:
        if isinstance(each_item,list):
            print_list(each_item)
        else:
            print each_item

#python         
>>> import nester           
>>> print nester.print_list.__doc__
这是函数文档字符串
>>> help(nester)
Help on module nester:
NAME
    nester - 这是模块文档字符串
FILE
    /python/nester.py
FUNCTIONS
    print_list(name)
        这是函数文档字符串
(END) 

参数:

调用函数时可使用的参数类型:
• 必备参数
• 关键字参数
• 默认参数
• 不定长参数

必备参数:

必备参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。
比如调用printme()函数,你必须传入一个参数,不然会出现语法错误:
#!/usr/bin/python
def printme( str ):
print str;
return;

#调用printme函数
printme();

输出结果:
Traceback (most recent call last):
File "test.py", line 11, in <module>
printme();
TypeError: printme() takes exactly 1 argument (0 given)

关键字参数:

关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值。
使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配
参数值。
以下实例在函数 printme() 调用时使用参数名:
#!/usr/bin/python3
def printme( str ):
print str;
return;

#调用printme函数
printme( str = "My string");

输出结果: 
My string

下例能将关键字参数顺序不重要展示得更清楚:
#!/usr/bin/python3
def printinfo( name, age ):
print "Name: ", name;
print "Age ", age;
return;

#调用printinfo函数
printinfo( age=50, name="miki" )

输出结果:
Name:  miki
Age  50

缺省参数:

调用函数时,缺省参数的值如果没有传入,则被认为是默认值。
下例会打印默认的age,如果age没有被传入:
#!/usr/bin/python3
def printinfo( name, age = 35 ):
print "Name: ", name;
print "Age ", age;
return;

#调用printinfo函数
printinfo( age=50, name="miki" );
printinfo( name="miki" )

输出结果:
Name:  miki
Age  50
Name:  miki
Age  35

不定长参数:

如果需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数,和上述2种参数不
同,声明时不会命名。
语法:
def functionname([formal_args,] var_args_tuple ):
function_suite
return [expression]
#加了星号(
)的变量名会存放所有未命名的变量参数。

例1:
#!/usr/bin/python3
def printinfo( arg1, *vartuple ):
print "输出: "
print arg1
for var in vartuple:
print var
return

# 调用printinfo 函数
printinfo( 10 );
printinfo( 70, 60, 50 )

输出结果:
    输出:
    10
    输出:
    70
    60
    50

例2:
#vim multi.py
def multiarg(*args):
for i in args:
print i
return

def multiargs(*args):
    print args[2]
    return

print "第一个调用:"
multiarg('hello','lili','tom','wing')
print "第2个调用:"
multiargs('hello','lili','tom','wing')        

执行结果:       
    第一个调用:
    hello
    lili
    tom
    wing
    第2个调用:
    tom

*args和**kwargs

[wing@macserver ~]$ cat b.py
#!/usr/bin/env python
def foo(*args,**kwargs):
    print 'args = ',args
    print 'kwargs = ',kwargs
    print '-'*20
if __name__ == '__main__':
    foo(1,2,3,4)                         
    foo(a=1,b=2,c=3)
    foo(1,2,3,4,a=1,b=2,c=3)    #同时使用*args和**kwargs,必须*args参数列在前
    foo('a',1,None,a=1,b='2',c=3)
    #*args是一个tuple,**kwargs表示关键字参数,是一个dict

执行结果:
[wing@macserver ~]$ python b.py
args =  (1, 2, 3, 4)
kwargs =  {}
--------------------
args =  ()
kwargs =  {'a': 1, 'c': 3, 'b': 2}
--------------------
args =  (1, 2, 3, 4)
kwargs =  {'a': 1, 'c': 3, 'b': 2}
--------------------
args =  ('a', 1, None)
kwargs =  {'a': 1, 'c': 3, 'b': '2'}
--------------------   

return 语句:

return [表达式] 用来退出函数,选择性地向调用方返回一个表达式。
不带参数值的return语句返回None
例:
#!/usr/bin/python3
def sum( arg1, arg2 ):
total = arg1 + arg2
return total

# 调用sum函数
print sum( 10, 20 )

输出结果:
30      

变量作用域:

一个程序所有的变量并不是在任何位置都可以访问的。访问权限决定于这个变量是在哪里赋值的。
变量的作用域决定了在哪一部分程序你可以访问哪个特定的变量名称。
调用函数时,所有在函数内声明的变量名称都将被加入到作用域中

两种最基本的变量作用域:全局变量和局部变量

定义在函数内部的变量拥有一个局部作用域
定义在函数外的变量拥有全局作用域

局部变量只能在其被声明的函数内部访问
全局变量可以在整个程序范围内访问

例:
#!/usr/bin/python
total = 0 # 这是一个全局变量
def sum( arg1, arg2 ):
total = arg1 + arg2 # total在这里是局部变量.
print "函数内是局部变量 : ", total
return total;

#调用sum函数
sum( 10, 20 );
print "函数外是全局变量 : ",total 

输出结果:
函数内是局部变量 :  30
函数外是全局变量 :  0           

命名空间和作用域

变量:
是拥有匹配对象的名字(标识符)。

命名空间:
是一个包含了变量名称们(键)和它们各自相应的对象们(值)的字典。

1.一个Python表达式可以访问局部命名空间和全局命名空间里的变量。
2.如果一个局部变量和一个全局变量重名,则局部变量会覆盖全局变量。
3.每个函数都有自己的命名空间。类的方法的作用域规则和通常函数的一样。
4.Python认为任何在函数内赋值的变量都是局部的。因此,如果要给全局变量在一个函数里赋
值,必须使用global语句。

global VarName表达式会告诉Python, VarName是一个全局变量,这样Python就不会在
局部命名空间里寻找这个变量了。

例如,在全局命名空间里定义一个变量money,再在函数内给变量money赋值,然后Python
会假定money是一个局部变量。然而,我们并没有在访问前声明一个局部变量money,结
果就是会出现一个UnboundLocalError的错误。取消global语句的注释就能解决这个问题。
#!/usr/bin/python
Money = 2000
def AddMoney():
#想改正代码就取消以下注释:
#global Money
Money = Money + 1

print Money
AddMoney()
print Money

globals()和locals()函数

根据调用地方的不同,globals()和locals()函数可被用来返回全局和局部命名空间里的名字。

在函数内部调用locals():
返回的是所有能在该函数里访问的命名。

在函数内部调用globals():
返回的是所有在该函数里能访问的全局名字。

两个函数的返回类型都是字典。所以名字们能用keys()函数摘取。

匿名函数与 lambda

python 使用 lambda 来创建匿名函数。
• 匿名是因为不需要以标准的方式来声明,比如说, 使用 def 语句。
• 一个完整的 lambda“语句”代表了一个表达式,这个表达式的定义体必须和声明放在同一行。
• lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
• lambda函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。
• lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。

语法:
lambda [arg1 [,arg2,.....argn]]:expression
参数是可选的,如果使用参数,参数通常也是表达式的一部分。
例:
#!/usr/bin/python
sum = lambda arg1, arg2: arg1 + arg2

# 调用sum函数
print "相加后的值为 : ", sum( 10, 20 )
print "相加后的值为 : ", sum( 20, 20 )

输出结果:
相加后的值为 :  30
相加后的值为 :  40

复习下单行语句:
def true():
return True
上面的函数没有带任何的参数并且总是返回 True。python 中单行函数可以和标题写在同一行。

重写true()函数:
    def true(): return True

使用 lambda 的等价表达式(没有参数,返回一个 True)为:
lambda :True

In [4]:     a = lambda x, y=2: x + y
In [5]:     a(5)
Out[5]:   7

In [6]:     a(3,5)
Out[6]:   8

In [2]:     a=lambda *z:z
In [3]:     a(1,2,3,4)
Out[3]:   (1, 2, 3, 4)

函数不带括号:

In [1]: def hello(a,b): #函数定义
...: print a + b
...:

In [2]: hello(1,2) #函数调用,带括号表示告诉编译器”执行这个函数“

In [6]: c=hello #不带括号表示把函数赋给另一个函数对象
In [8]: c(3,4)
7

递归函数

递归函数不需要任何修改就可以处理任意深度的嵌套列表

#!/usr/bin/env python
name=['wing',['tom',['jim',['lilei','han×××']]]]
def print_list(name):
    for each_item in name:
        if isinstance(each_item,list):
            print_list(each_item)
        else:
            print each_item

print_list(name)
def print_list(list_name,level=0):
    for each_item in list_name:
        if isinstance(each_item,list):
            print_list(each_item,level+1)
        else:
            for tab_num in range(level):
                print "\t",
            print each_item

print_list(name)
题目:利用递归方法求5!。                     
程序分析:递归公式:fn=fn_1*4!
程序源代码:
#!/usr/bin/python
# -*- coding: UTF-8 -*-

def fact(j):
    sum = 0
    if j == 0:
        sum = 1
    else:
        sum = j * fact(j - 1)
    return sum

for i in range(5):
    print '%d! = %d' % (i,fact(i))
以上实例输出结果为:
0! = 1
1! = 1
2! = 2
3! = 6
4! = 24

内建函数

Python中,按照对象是否可变,将类型分类为:

不可变类型:
对象的内容不能够改变(not mutable),这些类型中主要有数值类型(整数,浮点数,复数),字符
串类型,元组等
可变类型:
对象的内容能够改变(mutable),主要有列表,字典

Python针对众多的类型,提供了众多的内建函数来处理(内建是相对于导入import来说的,后面学
习到包package时,将会介绍),

这些内建函数功用在于其往往可对多种类型对象进行类似的操作,即多种类型对象的共有的操作;
如果某种操作只对特殊的某一类对象可行,Python常将其设置为该种类型的方法(method)

内建函数的查看
通过在python交互模式下,键入相应的命令即可查看当前python版本的一些内建函数
>>> dir(__builtins__)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs', 'all', 'any', 'apply', 'basestring', 'bin', 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']

也可以通过如下方式查看:
    >>> import __builtin__
    >>>dir(__builtin__)

获取内建函数帮助:

help()  
获取帮助信息

其完整的一般使用形式为:
help(module.class.function)

例子:
In [3]: import sys
In [4]: help(sys.getsizeof)

数值类型表示的内建函数

bin()     获取一个整数(int类型或长整型),返回其2进制形式的字符串
oct()     获取一个整数,返回其8进制形式的字符串
hex()    获取一个整数,返回其16进制形式的字符串

对象生成的内建函数

int() 将数值或字符串转换为整数int,完整使用形式int(x,base),base用于指定进制
long() 将数值或字符串转换为整数long,完整使用形式long(x, base),base用于指定进制
float() 将数值或字符串转换为浮点数
complex()返回一个复数,完整使用形式 complex(real,imag)
str() 将所给对象转换为字符串,使用形式为str(object)
list() 获取对象,转换为列表, list(object)
dict() 获取映射转换为字典,dict(mapping)
tuple() 获取一个可迭代的对象,返回一个元组, tuple(iterable)
注:这里留意dict()内建函数创建字典的不同方式

装饰器Decorator

装饰器是我们现在碰到的难点之一,不大好理解,可以简单的认为装饰器就是为了给函数添加额外的功能,而不用每次都手写那个功能,只需要用"@装饰器名称"调用就可以了

不使用装饰器给函数添加一个额外的功能:
下例中是给foo函数添加日志功能

#!/usr/bin/env python3
import logging
def foo():
        print "hello world"

def use_logging(func):
        logging.warn("%s is running" % func.__name__)
        func()

use_logging(foo)   #每次执行foo的时候都得调用use_logging函数

使用装饰器:
@use_logging #装饰器
foo()

简陋写法:因为不能给带有参数的函数装饰,所以简陋
#!/usr/bin/env python
#coding=utf8
import logging

def use_logging(func):
    logging.warn("%s is running" % func.__name__)
    return func

@use_logging
def foo():
    print "hello world"

foo()

标准写法:
    #!/usr/bin/env python
    #coding=utf8
    import logging

    def use_logging(func):
        def wrapper(*args,**kw):
            logging.warn("%s is running" % func.__name__)
            return func()
        return wrapper

    @use_logging
    def foo():
        print "hello world"

    foo()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
带参数:
#!/usr/bin/env python
#coding=utf8
import logging

def use_logging(func):
    def wrapper(*args,**kw):   
        logging.warn("%s is running" % func.__name__)
        return func()               #会报错
    return wrapper

@use_logging
def foo(arg1,arg2):
    print "arg1+arg2=%s" % (arg1+arg2)

foo(5,3)

正确写法如下:
#!/usr/bin/env python
#coding=utf8
import logging

def use_logging(func):
    def wrapper(*args,**kw):      
        logging.warn("%s is running" % func.__name__)
        return func(*args,**kw)   
    return wrapper

@use_logging
def foo(*args,**kw):
    print "arg1+arg2=%s" % (args[0]+args[1])
    print "hello world"

foo(1,2)
因为装饰器会先执行,在没有wrapper函数的情况下return func(*args,**kw)会出现args未定义错误