Lucas

导航

步入函数式编程的大门

注:本人用Python3.4作为学习版本,以下学习心得只适用于Python3.4。

写下这篇博客,意味着我即将步入函数式编程的大门。

说起函数,首先想到的莫过于读书至今依然围绕在我身边的那个“函数”。数学计算中的函数,简单地说,就是对给定数的一种人为约定的运算法则,将给定的数通过某种变化得到一个新的结果。Python中的函数有内置函数,简单的说和数学上的函数功能类似,比如说abs,就是将给定的值取绝对值,验证如下:

# Author: Lucas w
res = abs(-10)
print(res)

运行结果如下所示:

10

Process finished with exit code 0

类似的函数在Python中有好多,函数式编程和数值运算中的函数有着相同的灵魂,作为初级入门,我暂且将函数式编程定义为:将一段代码封装起来,就变成一个函数,起个名字,就是函数名,当需要调用这段代码时,直接调用函数名即可。以下是验证程序:

# Author: Lucas w
#一段代码
a=1
b=2
c=a+b
print(c)
#将代码封装变成一个简单函数
def res():
    a=1
    b=2
    c=a+b
    print(c)
res()

结果如下:

3
3

Process finished with exit code 0

说了这么多,接下来谈一下函数的基本形式,代码如下:

# Author: Lucas w
def test(x):
    x+=1
    print(x)
    return 0
test(1)

运行结果如下:

2

Process finished with exit code 0

其中test表示函数名,x为形式参数,return为函数的返回值,这就是函数最简单的标准形式。接下来我们对函数进行全面的剖析。

首先我们来探讨函数的返回值return。

当我们需要接收函数返回值时,我们需要将函数赋给一个变量。验证程序如下:

# Author: Lucas w
def test():
    return 0
y= test()
print(y)

运行结果如下:

0

Process finished with exit code 0

接下来要考虑的问题是,return都可以返回什么样的值。经过学习,初步确定为三种:当没有定义返回值时,返回空值None;当定义为对象时,返回该对象;当定义不止一个对象时,返回一个元组。验证程序如下:

# Author: Lucas w
def test1():
    return
x= test1()
def test2():
    return 1
y= test2()
def test3():
    return 1,2,3,4,5
z= test3()
print(x)
print(y)
print(z)

运行结果如下:

None
1
(1, 2, 3, 4, 5)

Process finished with exit code 0

至此,我们已经熟悉了函数return都可以返回的值,至于定义函数为什么要加return这个返回值,大概是某些特定的时候,需要得到函数的计算结果,所以return才应运而生,我们姑且这么理解,毕竟刚入门,不可能追究过深。

接下来,我们来挖掘一下函数的参数所蕴含的秘密。

说起参数,我们都知道函数有形式参数,有实际参数。至于这两者的区别,一句话概括:实际参数占用内存空间,而形式参数不占用内存空间。这里我们用查询内存地址函数id来验证,程序如下:

# Author: Lucas w
def test(x):
    print(x)
    print(id(x))
    return
test(1)
print(id(1))

运行结果如下:

1
1755480256
1755480256

Process finished with exit code 0

可以看出,当函数运行时形式参数和实际参数有相同的内存地址。但是当函数运行后,形式参数被释放,不占用内存;而实际参数依然在内存中存在。验证如下:

# Author: Lucas w
def test(x):
    print(x)
    return
test(1)
print(id(1))
print(id(x))

运行如下:

1
1755480256
Traceback (most recent call last):
    print(id(x))
NameError: name 'x' is not defined

Process finished with exit code 1

可以看出,并没有在内存中找到x。这就看出了形式参数和实际参数的区别。形式参数用时占用内存,不用时释放内存;而实际参数,只要定义了,就一直在内存中存在。

接下来,我们来探讨参数的调用。这里主要有两种调用方式:位置参数调用,顾名思义就是按照函数定义时参数的位置,采用一一对应的原则进行调用;另一种是关键字调用,这种调用方式更加灵活。验证程序如下:

# Author: Lucas w
def test(x,y,z):
    print(x)
    print(y)
    print(z)
    return
print("位置参数调用")
test(1,2,3)
print("关键字调用")
test(x=1,z=3,y=2)

运行结果如下:

位置参数调用
1
2
3
关键字调用
1
2
3

Process finished with exit code 0

另外需要注意的是,关键字参数只能写在位置参数后边,不然程序就会报错。验证如下:

# Author: Lucas w
def test(x,y,z):
    print(x)
    print(y)
    print(z)
    return
print("位置参数在关键字参数前,可以调用")
test(1,y=2,z=3)

运行结果如下:

位置参数在关键字参数前,可以调用
1
2
3

Process finished with exit code 0

位置参数在关键字参数后,则不能完成函数调用,验证程序如下:

# Author: Lucas w
def test(x,y,z):
    print(x)
    print(y)
    print(z)
    return
print("位置参数在关键字参数后,不可以调用")
test(x=1,2,3)

运行结果如下:

    test(x=1,2,3)
            ^
SyntaxError: non-keyword arg after keyword arg

Process finished with exit code 1

因此,在调用函数时只要记住一句话:位置参数在前,关键字参数在后。

当我们需要定义函数的参数,不只局限于x,y...登有限个参数时。Python提供了两种方式:一种是元组存储法;一种是字典存储法。

接下来我们来看元组存储法,程序如下:

# Author: Lucas w
def test(*args):
    print(args)
    return
test(1,2,3,4,5,6,7,8,9,0)

运行结果如下:

(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)

Process finished with exit code 0

以此可知,我们输入的参数,都存储在了args这个tuple中,并且并不限制参数数目。

接下来我们来验证第二种方法,字典存储法。验证程序如下:

# Author: Lucas w
def test(**args):
    print(args)
    return
test(name="Wang",age="18",sex="men")

运行结果如下:

{'sex': 'men', 'name': 'Wang', 'age': '18'}

Process finished with exit code 0

因此,当我们需要定义的函数有未知个参数时,可以采取两种方法。一种是定义为元组,采用*args;一种是定义为字典,采用**args。

至此,函数式编程基本形式,我们已经有了大概的了解。接下来明确一下局部变量和全局变量的关系。

在Python中,

全局变量指的是:除函数内定义的变量之外,其余程序中所有该变量都代表同一个变量。全局变量一般在程序一开始定义。

局部变量指的是:函数内定义的变量,生效范围只在函数内,当函数不运行时,该变量不生效。

另外需要注意的是,如果同时将一个变量定义为全局变量和局部变量,那么程序在执行时,函数内将其作为局部变量,函数外将其作为全局变量。实际运行时,也就是同名,实际代表的功能并不一样。这就好比老王有个儿子叫小王,而班里还有一个同学叫小王,我们并不能说这两个小王都是老王的儿子,也仅仅只是同名而已。慢慢体会,似乎同名的全局变量和局部变量也是一样的道理。验证程序如下:

# Author: Lucas w
XiaoWang = "小王的同学"
def test(x):
    XiaoWang = "老王的儿子"
    print("局部变量")
    print(XiaoWang)
    return
test(XiaoWang)
print("全局变量")
print(XiaoWang)

运行结果如下:

局部变量
老王的儿子
全局变量
小王的同学

Process finished with exit code 0

这个全局变量和局部变量重名一般不会出现,之所以拿出来比较,也仅仅是加深理解概念罢了,并没有实际意义。另一个更加鸡肋的功能,将局部变量转换为全局变量。

这个用法,在Python中存在,但是实际用处并不大,只能将我等入门级程序员弄的满脸蒙蔽。转换方法如下所示:

# Author: Lucas w
XiaoWang = "小王的同学"
def test(x):
    global XiaoWang
    XiaoWang = "老王的儿子"
    print("转换为全局变量的局部变量")
    print(XiaoWang)
    return
test(XiaoWang)
print("全局变量")
print(XiaoWang)

运行结果如下:

转换为全局变量的局部变量
老王的儿子
全局变量
老王的儿子

Process finished with exit code 0

这个global功能,暂且知道用法,不进行细究。接下来我们进入递归函数的学习。

所谓递归函数,字面意思可以理解为函数自身来调用自身,需要注意的是,Python中默认递归次数为999,虽然说我们可以通过设置来增加递归次数,但这显然不是最理想的做法,因为我们打破了Python对系统的一种保护机制。因此,我们在使用递归函数时必须明确指定一个结束条件,以免造成对系统损坏。以下是一种简单的递归函数:

# Author: Lucas w
def test(x):
    print(x)
    x-=1
    if x>0:
        return test(x)
test(5)

运行结果如下:

5
4
3
2
1

Process finished with exit code 0

了解完递归函数之后,进入最后一个知识点:高阶函数。

所谓高阶函数,就是将运算法作为参数引入函数的定义中,通过以下例子,我们就可以直观的看到。验证程序如下:

# Author: Lucas w
def test(x,y,f):
    return f(x)+f(y)
res=test(1,-3,abs)
print(res)

运行结果如下:

4

Process finished with exit code 0

自此,我们对Python中函数的概念有了一个大致的了解,算作是小小的入门吧,毕竟只是了解了一些概念,随着学习的深入,慢慢进步,共勉。

 

posted on 2017-09-22 20:27  莲蓉馅的和氏璧  阅读(225)  评论(0编辑  收藏  举报