python基础3
1、函数基本语法及特性
2、函数参数
3、局部变量和全局变量
4、返回值
嵌套函数
5、递归函数
6、匿名函数
7、高阶函数
8、内置函数
9、函数式编程

在编程语言中的函数并非数学意义上的函数(总是返回根据参数计算得到的结果),编程语言中的函数也称为过程,在使用这个函数的时候有可能不会返回一个结果或者说返回的结果不是数学意义上的函数执行的结果。

一、函数基本语法及特性

1、抽象和结构

抽象是程序能够被别人理解的关键所在(无论对编写程序还是阅读程序来说,这都至关重要)。
程序应非常抽象,如下载网页、计算使用频率、打印每个单词的使用频率。下面将上述简单描述转换为一个Python程序:

page = download_page()
freqs = compute_frequencies(page)
for word, freq in freqs:
    print(word, freq)

看到这些代码,任何人都知道这个程序是做什么的。至于具体该如何做那么我们就得在函数中得出定义。

在比如打印斐波那契数(一种数列,其中每个数都是前两个数的和)

fibs = [0,1]
for i in range(8):
    fibs.append(fibs[-2] + fibs[-1])
print(fibs)

上述代码会打印出斐波那契数的前十个,当然你也可以指定要打印数的个数:

num = int(input("How many Fibonacci numbers do you want?"))
fib = [0,1]
for i in range(num-2):
    fibs.append(fibs[-2] + fibs[-1])
print(fibs)

当我们在其他地方想使用这些数的时候,你可能会想到我再把这个循环再写一次,对于这段代码是可以的,但是对于更加复杂的代码你还愿意去重写吗?真正的程序员会去抽象程序,把一些想要实现的功能抽象为函数,直接调用这个函数来实现指定的功能,让程序更加简洁易懂并且降低代码的重复率。真正的程序员会像下面这样做:

num = int(input("How many Fibonacci numbers do you want?"))
print(fibs(num))

2、函数的定义

def 函数名():
    函数体

例如:

def hello():
    print("Hello world!")

3、给函数编写文档

要给函数编写文档,以确保其他人能够理解,可添加注释。放在函数开头的字符串成为文档字符串(docstring),将作为函数的一部分存储起来。例如:

def square(x):
    'Calculates the square of the number x.'
    return x * x

可以像下面这样访问文档字符串:

>>> square.__doc__
'Calculates the square of the number x.'

特殊的内置函数help很有用。在交互式解释器中,可使用它获取有关函数的信息,其中包含函数的文档字符串。

>>> help(square)
Help on function square in module __main__:
square(x)
Calculates the square of the number x.

二、函数的参数

为了让一个函数更通用,在定义函数的时候可以让函数接收数据,函数接收的数据就是函数的参数。

1、参数的基本应用

(1)定义带参数的函数


示例如下:

def add(a,b):
    c = a + b
    print(c)

(2)调用带参数的函数


以上面的add函数为例,我们调用该函数,代码如下:

def add(a,b):
    c = a + b
    print(c)
   
add(11,22)   #调用带参数的函数时,需要在小括号中传递数据

(3)调用函数时参数的顺序


>>> def test(a,b):
...     print(a,b)
... 
>>> test(1,2)
1 2
>>> test(b=1,a=2)
2 1
>>> 
>>> test(b=1,2)
  File "<stdin>", line 1
SyntaxError: positional argument follows keyword argument
>>> 
>>>

通过以上代码我们可以看出,函数的参数在调用的过程中是一一对应的,如果不指定那个实参是那个对应的形参时,会默认按实参的顺序将实参赋值给形参。

(4)总结


  • 定义时小括号中的参数,用来接收参数用的,称为“形参”
  • 调用时小括号中的参数,用来传递给函数的,称为“实参”

2、参数的高级应用

(1)缺省参数


调用函数时,缺省参数的值如果没有传入,则被认为是默认值。缺省参数就是给参数设定默认值,如果在传参过程中,缺省参数传入值的话则依照传入的值,如果没有传入值则按照默认的值。如下代码:

def sum(a,b=11):
    result = a + b 
    print("%d+%d=%d"%(a,b,result))

sum(11,22)    #调用函数时会将11和22分别赋值给a和b
sum(10,)      #调用函数时,由于只给了一个实参,则10赋值给a,b则用默认的参数值11

程序运行结果如下:

features函数python python frequency函数_局部变量

注意:带有默认值的参数一定要位于参数列表的最后面。

>>> def printinfo(name, age=35, sex):
...     print name
...
  File "<stdin>", line 1
SyntaxError: non-default argument follows default argument

(2)不定长参数


有时可能需要一个函数能处理比当初声明时更多的参数,这些参数叫做不定长参数,声明时不会命名。

基本语法如下:

def functionname([formal_args,] *args, **kwargs):
    "函数_文档字符串"
    function_suite
    return [expression]
加*号的变量args会存放所有未命名的变量参数,args为元组
加**号的变量kwargs会存放命名参数,即形如key=value的参数,kwargs为字典

可变参数示例:

>>> def fun(a, b, *args, **kwargs):
...     """可变参数演示示例"""
...     print "a =", a
...     print "b =", b
...     print "args =", args
...     print "kwargs: "
...     for key, value in kwargs.items():
...         print key, "=", value
...
>>> fun(1, 2, 3, 4, 5, m=6, n=7, p=8)  # 注意传递的参数对应
a = 1
b = 2
args = (3, 4, 5)
kwargs: 
p = 8
m = 6
n = 7
>>>
>>>
>>> c = (3, 4, 5)
>>> d = {"m":6, "n":7, "p":8}
>>> fun(1, 2, *c, **d)    # 注意元组与字典的传参方式
a = 1
b = 2
args = (3, 4, 5)
kwargs: 
p = 8
m = 6
n = 7
>>>
>>>
>>> fun(1, 2, c, d) # 注意不加星号与上面的区别
a = 1
b = 2
args = ((3, 4, 5), {'p': 8, 'm': 6, 'n': 7})
kwargs:
>>>
>>>

(3)引用传参


  • 可变类型与不可变类型的变量分别作为函数参数时,会有什么不同?
  • Python有没有类似C语言中的指针传参?
>>> def selfAdd(a):
...     """自增"""
...     a += a
...
>>> a_int = 1
>>> a_int
1
>>> selfAdd(a_int)
>>> a_int
1
>>> a_list = [1, 2]
>>> a_list
[1, 2]
>>> selfAdd(a_list)
>>> a_list
[1, 2, 1, 2]

Python中函数参数是引用传递(注意不是值传递)。对于不可变类型,因变量不能修改,所以运算不会影响到变量自身;而对于可变类型来说,函数体中的运算可能会更改传入的参数变量。

如下代码示例:

>>> def selfAdd(a):
...     """自增"""
...     a = a + a   # 我们更改了函数体的这句话
...
>>> a_int = 1
>>> a_int
1
>>> selfAdd(a_int)
>>> a_int
1
>>> a_list = [1, 2]
>>> a_list
[1, 2]
>>> selfAdd(a_list)
>>> a_list
[1, 2]      # 想一想为什么没有变呢?

Python中变量之间的赋值利用到引用,只要是有"="就是引用,引用就是变量中存储的是地址,而地址指向的内存空间是存真实数据地方。a = a + a会创建一个新的内存空间,然后让变量a指向这段空间;而a += a是将a所指向的那段空间的值变为a + a。如下demo:

>>> a = [1,2]
>>> id(a)
31329224
>>> a = a + a
>>> a
[1, 2, 1, 2]
>>> id(a)
31991944
>>> b = 2
>>> id(b)
1587990368
>>> b = b + b
>>> b
4
>>> id(b)
1587990432
>>> c = [1,2]
>>> id(c)
31992072
>>> c += c
>>> c
[1, 2, 1, 2]
>>> id(c)
31992072
>>>

三、局部变量和全局变量

1、局部变量

  • 局部变量,就是定义在函数内部的变量,其作用单位只是在函数体内;
  • 不同的函数,可以定义相同名字的局部变量,其不会产生影响;
  • 局部变量的作用:为了临时保存数据需要在函数中定义变量来进行存储。

示例:

def test1():
    a = 100     #此时的a为局部变量,作用范围只在test1函数内
def test2():
    print("a=%d"%a)
    
test1()
test2()

此时由于a是局部变量,其做作用范围只在test1函数中,相当于test2函数中没有定义变量a,所以上述程序在运行时会报如下错误:

features函数python python frequency函数_features函数python_02

2、全局变量

(1)什么是全局变量


如果一个变量既能在函数体中使用,也能在其他函数中使用,这样的变量称为全局变量

示例:

#定义全局变量
a = 100

def test1():
    print("a=%d"%a)

def test2():
    print("a=%d"%a)

#调用函数
test1()
test2()

由于变量a是全局变量,所以在test1和test2两个函数中都可以使用变量a,运行结果如下:

features函数python python frequency函数_全局变量_03

(2)全局变量与局部变量同名的问题


当全局变量与局部变量同名时,在定义该局部变量的函数体内以局部变量为主,如下示例:

#定义全局变量
a = 100 

def test1():
    a = 300 
    print("---test1--修改前--a=%d"%a)
    a = 200 
    print("---test1--修改后--a=%d"%a)

def test2():
    print("---test2---a=%d"%a)

test1()
test2()

此时,对于test1函数,函数体内定义了和全部变量同名的局部变量a,此时会以函数中的变量a为根据;对于test2函数,函数体内没有定义和全局变量同名的局部变量,所以如果test2函数使用变量a,则以全部变量a为根据。运行结果如下:

features函数python python frequency函数_python_04

(3)修改全局变量


既然是全局变量,就可以在所有的函数中是,那么是否可以修改呢?看如下代码:

#定义全局变量
a = 100 

def test1():
    global a
    print("---test1--修改前--a=%d"%a)
    a = 200 
    print("---test1--修改后--a=%d"%a)

def test2():
    print("---test2---a=%d"%a)

test1()
test2()

代码运行结果如下:

features函数python python frequency函数_局部变量_05

在函数体内利用global这个关键字来声明变量为全局变量,此时在函数体内对该变量的操作就是相当于对全局变量的操作。

(4)可变类型的全局变量


看下面两个代码示例的区别:

#定义全局变量
a = 100 

def test1():
    print("---test1--修改前--a=%d"%a)
    a = 200 
    print("---test1--修改后--a=%d"%a)

def test2():
    print("---test2---a=%d"%a)

test1()
test2()

上述代码运行结果为:

features函数python python frequency函数_c/c++_06

#定义全局变量
a = [11,22,33]

def test1():
    print("---test1--修改前--",a)
    a.append(44)
    print("---test1--修改前--",a)

def test2():
    print("---test2--修改前--",a)

test1()
test2()

上述代码运行结果为:

features函数python python frequency函数_python_07

对于上述两种情况,列表(list)、字典(dict)这种可变的类型的全局变量不用再函数体内声明全局变量,在函数体中对该变量的操作就是在对全局变量进行操作;而像数值类型(int,flaot,bool,long)、字符串(str)、元组(tuple)这种不可变类型的操作,要想在函数体内对这种全局变量进行操作,必须在函数体内声明其为全局变量。

(5)总结


  • 在函数外边定义的变量叫全局变量
  • 全局变量能够在所有的函数中进行访问
  • 如果在函数中修改全局变量,那么就需要使用global进行声明
  • 如果全局变量的名字和局部变量的名字相同,那么使用的是局部变量的,小技巧强龙不压地头蛇
  • 在函数中不使用global声明全局变量时不能修改全局变量的本质是不能修改全局变量的指向,即不能将全局变量指向新的数据
  • 对于不可变类型的全局变量来说,因其指向的数据不能修改,所以不能使用global时无法修改全局变量
  • 对于可变类型的全局变量来说,因其指向的数据可以修改,所以不使用global时也可修改全局变量

四、函数的返回值

“返回值”,就是程序中函数完成一件事情后,最后给调用者返回的结果。

1、带有返回值的函数

想要在函数中把结果返回给调用者,需要在函数中使用return
如下示例:

def add2num(a, b):
    c = a+b
    return c
或者
def add2num(a, b):
    return a + b

2、保存函数的返回值

在程序中,如果一个函数返回了一个数据,那么想要用这个数据,就需要用一个变量来保存函数返回的数据。示例如下:

#定义函数
def add2num(a, b):
    return a+b

#调用函数,顺便保存函数的返回值
result = add2num(100,98)

#因为result已经保存了add2num的返回值,所以接下来就可以使用了
print result

3、Python返回多值
看如下代码:

def test(a,b):
    sum2 = a + b 
    mulit = a * b 
    return [sum2,mulit]

result = test(2,3)
print(result)

运行结果为:

[5,6]
def test(a,b):
    sum2 = a + b 
    mulit = a * b 
    return sum2,mulit

result = test(2,3)
print(result)

运行结果为:

(5,6)

由上述示例我们可以得出,当有多个返回值时,利用列表将结果返回,那么函数就会返回一个列表;当将几个返回值用逗号隔开返回时,默认返回的是元组。