# 函数

函数是执行某一任务的一段代码,通过将一段代码定义成函数,并指定函数名,在需要的时候通过函数名来调用这段代码,函数是代码复用的重要手段。

函数的定义形式如下:

def 函数名(形参列表):
    [return [返回值]]

#样例如下:

def findMax(x,y):
    '''
    获取两个数中比较大的那个数

    findMax(x,y)
        x,y为要比较的两个数
    '''
    return x if x > y else y

print(findMax(1,2))

help(findMax)
print(findMax.__doc__)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

上面提供了函数的说明文档,这样使用help时就能查看它的用法,这样有助于理解长时间没有使用的函数。当函数有多个返回值时会返回一个元组:

def getValue(a,b):
    return a,b

print(getValue(1,2)[1])
1
2
3
4

# 递归函数

在一个函数体内调用自身,就称为函数递归,递归思想是解决不了当前问题时,去尝试解决跟他有关系的更简化的问题,直到能解决的问题,这个过程需要递推公式来不断的递归到能解决的问题:

def sum(n):
    if n==1 :
        return 1
    else:
        return sum(n-1)+n

print(sum(6))
1
2
3
4
5
6
7

# 函数参数

Python的函数参数可以为函数提供输入数据,这样函数根据不同的输入来运行。

按照形参位置来传入参数被称为位置参数,按照参数名来传入参数则是关键字(keyword)参数,使用关键字参数可以不管形参定义时的顺序,可以混用关键字参数和位置参数,但是混用时关键字参数必须在位置参数后面。

定义形参时,可以为形参指定默认值,但是指定了默认值的形参必须在没有默认值的参数后面。看下面示例:

def getArea(name,width=1,height=2):
    print("name: %s,width: %i,height: %i" % (name,width,height))
    return width*height

getArea("square1")
getArea("square1",10)
getArea("square1",height=20)
1
2
3
4
5
6
7

Python的参数个数可以是不确定的,这就是可变形参,可变形参都会被放入到一个元组中,也可以放到一个字典中:

def getBookList(author,*books,num=10):
    for book in books:
        print(book)
    print("author %s,nubmer: %s" %(author,num))

getBookList("li","chinese","math","english",20)
getBookList("li","chinese","math","english",num=20)

def varInput(a,b,c=3,*inputTuple,**inputDict):
    print(a,b,c)
    print(inputTuple)
    print(inputDict)

varInput(1,2,"test1","test2",ke1="test3",ke2="test4")
varInput(1,2,ke1="test3",ke2="test4")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Python中还有逆向参数收集的概念,指的是程序在已有列表,元组,字典等对象的前提下把它们的元素拆开来传给函数参数。

def testList(name,num,*other):
    print("name: %s,num: %s" %(name,num))
    print(other)

#列表
list1=["li",10,"hi","world"]
testList(*list1)

#元组
tuple1=("wang",20,"hello","nice")
testList(*tuple1)

#字典
dict1={'num':30,'name':"sun"}
testList(**dict1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 值传递

Python中形参的传递是值传递,即将变量的值传递给形参变量,而形参变量均为新开辟的变量。

def swap(a,b):
    a,b=b,a
    print("in swap function,a: %s,b %s" % (a,b))

a=1
b=2
swap(a,b)
print("in main,a: %s,b %s" % (a,b))
1
2
3
4
5
6
7
8

但是把列表或字典传给形参时,虽然依旧是值传递,但是这时传递的是变量地址,不是变量内容。

def swap(dict1):
    dict1["a"],dict1["b"] = dict1["b"],dict1["a"]
    dict1=None
    print(dict1)

dict1={"a":1,"b":2}
swap(dict1)
print(dict1)
1
2
3
4
5
6
7
8

# 局部函数

前面的函数都是在全局范围内定义的,它们都是全局函数,Python支持在局部作用域中定义函数,这种函数被称为局部函数。

默认情况下,局部函数对全局作用域是隐藏的,局部函数只在局部作用域内有效。注意如果封闭函数返回它的局部函数,这个局部函数被保存在一个变量中,这些局部作用域的函数就会被扩大:

def sealedFunc():
    num=100
    def getNum():
        num=101
        def setNum():
            nonlocal num
            num=102
        setNum()
        print(num)
    getNum()
    print(num)
    return getNum

sealedFunc()
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 函数变量

Python中的函数也可以看成是一种值,所有函数都是function对象,可以把一个函数赋给一个变量,就像一个整数一样:

def addFunc(a,b):
    return a+b
def multiFunc(a,b):
    return a*b

def operTwoNumber(a,b,func):
    return func(a,b)

print(operTwoNumber(2,3,multiFunc))
print(operTwoNumber(2,3,addFunc))
1
2
3
4
5
6
7
8
9
10

# Lambda表达式

由于上面的局部函数定义完之后,对外部是不可见的,它的函数名在内部使用又有限,此时可以使用Lambda表达式,它的本质是匿名的,单行函数体的函数。相对于单行函数体,它省去了定义函数的过程,让代码更简洁,另外对于不需要多次复用的函数,lambda表达式可以在用完之后立即释放,提高了性能。

def getFunc(typeinfo):
    if typeinfo=="add":
        return lambda a,b:a+b
    if typeinfo=="multi":
        return lambda a,b:a*b

print(getFunc("multi")(2,3))

map1 = map(lambda x : x if x % 2 == 0 else None,range(10))
print(list(map1))

# 下面两种形式也是等价的:
def func(x):
    return x+100
list(map(func,range(10)))
## 等价于
list(map(lambda x:x+100,range(10)))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 变量作用域

在Python中,变量分为两种:

  • 局部变量:在函数中定义的变量,包括参数。
  • 全局变量:在函数外面定义的变量。

每个函数执行时都会,系统会给函数分配一块"临时内存空间",所有的局部变量都被保存在这块临时内存空间中,当函数执行完,这块临时内存空间就被释放,局部变量也都因释放而失效。全局变量意味着它们可以在所有的函数中被访问。

全局变量,局部变量会各被存储在一个字典中,要输出相应的字典,可以使用这三个工具函数:

  • globals():返回全局范围所有变量。
  • locals():返回当前范围的所有变量,如果在全局范围内使用这个函数,也会返回全局范围内的所有变量。
  • vars(object):返回指定对象范围内的所有变量,如果不传入object参数,它的作用同locals()。
  • nonlocal:返回外部嵌套函数内的变量,不是局部变量,也不是全局变量。

注意:

  • locals()和globals()获取全局范围内的变量字典时,是可以修改的并且可以影响全局变量。但是locals()来获取局部范围内的变量字典时,即使修改,也不会影响局部变量。
var=100

def testScope():
    localVar=1000
    locals()["localVar"]=888
    print("locals in testScope:%s" % locals())
    print("globals in testScope:%s" % globals())

testScope()

locals()["var"]=999
print("locals:%s" % locals())
globals()["var"]=1000
print("globals:%s" % globals())
print("vars:%s" % vars())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

注意如果在局部作用域引用过全局作用域的变量,此时局部作用域中再使用该变量就会报错:

var=100
def testScope():
    print(var)
    var=1000
    print(var)

testScope()
print(var)
1
2
3
4
5
6
7
8

如果将testScope中的第一行print语句注释掉,就不会报错,程序会在testScope中新建一个局部变量,然后输出该变量。要解决这个问题,有两种方式,一是使用globals()来访问var中的内容:

var=100
def testScope():
    print(globals()["var"])
    var=1000
    print(var)

testScope()
print(var)
1
2
3
4
5
6
7
8

另一种是直接将该变量指定为是对全局变量的引用:

var=100
def testScope():
    global var
    var=1000
    print(var)

testScope()
print(var)
1
2
3
4
5
6
7
8

最后是获取外部函数的变量:

num1 = 1
def test():
    num1 = 2
    def test1():
        nonlocal num1
        num1 = 3
    test1()
    print("in test: %s",num1)

test()
print("int global:%s",num1)
1
2
3
4
5
6
7
8
9
10
11

# 闭包函数

闭包是能够读取其他函数内部变量的函数,一般是函数内部的子函数,在子函数中,包含外层函数定义的变量(不是在子函数且不是在全局上下文),它的意义:

  1. 保护函数内的变量安全,要访问函数内的变量,只有通过闭包访问。
  2. 在内存中持久化一个变量,该变量会一直在内存中,不像函数的局部变量,运行完就释放。

来看实例:

def tester(start):
    def nested(label):
        print(label,nested.state)
        nested.state +=1
    nested.state = start
    return nested

F = tester(1)
F('hello')
F('world')
F('this')
F('is')
F('future')
1
2
3
4
5
6
7
8
9
10
11
12
13