当前位置: 首页 > 文档资料 > 跟老齐学 Python >

函数(1)

优质
小牛编辑
131浏览
2023-12-01

谁能使我们与基督的爱隔绝呢?难道是患难么,是困苦么,是逼迫么,是饥饿么,是赤身露体么,是危险么,是刀剑么。然而靠着爱我们的主,在这一切的事上,已经得胜有余了。因为我深信无论是死、是生、是天使、是掌权的,是有能的,是现在的事,是将来的事,是高处的,是低处的,是别的受造之物,都不能叫我们与神的爱隔绝。这爱是在我们的主基督耶稣里的。(ROMANS 8:35,37-39)

函数(1)

函数,对于人类来讲,能够发展到这个数学思维层次,是一个飞跃。可以说,它的提出,直接加快了现代科技和社会的发展,不论是现代的任何科技门类,乃至于经济学、政治学、社会学等,都已经普遍使用函数。

下面一段来自维基百科(在本教程中,大量的定义来自维基百科,因为它真的很百科):函数词条

函数这个数学名词是莱布尼兹在1694年开始使用的,以描述曲线的一个相关量,如曲线的斜率或者曲线上的某一点。莱布尼兹所指的函数现在被称作可导函数,数学家之外的普通人一般接触到的函数即属此类。对于可导函数可以讨论它的极限和导数。此两者描述了函数输出值的变化同输入值变化的关系,是微积分学的基础。

中文的“函数”一词由清朝数学家李善兰译出。其《代数学》书中解释:“凡此變數中函(包含)彼變數者,則此為彼之函數”。

函数,从简单到复杂,各式各样。前面提供的维基百科中的函数词条,里面可以做一个概览。但不管什么样子的函数,都可以用下图概括:

理解函数

在中学数学中,可以用这样的方式定义函数:y=4x+3,这就是一个一次函数,当然,也可以写成:f(x)=4x+3。其中x是变量,它可以代表任何数。

当x=2时,代入到上面的函数表达式:f(2) = 4*2+3 = 11所以:f(2) = 11

但是,这并不是函数的全部,在函数中,其实变量并没有规定只能是一个数,它可以是馒头、还可是苹果,不知道读者是否对函数有这个层次的理解。请继续阅读即更深刻

变量不仅仅是数

变量x只能是任意数吗?

其实,一个函数,就是一个对应关系。

读者尝试着将上面表达式的x理解为馅饼,4x+3就是4个馅饼在加上3(一般来讲,单位是统一的,但你非让它不统一,也无妨),这个结果对应着另外一个东西,那个东西比如说是iphone。或者说可以理解为4个馅饼加3就对应一个iphone。这就是所谓映射关系。

所以,x,不仅仅是数,可以是你认为的任何东西。

变量本质——占位符。

函数中为什么变量用x?这是一个有趣的问题,自己google一下,看能不能找到答案。很巧,在“知乎”上还真有人询问这个问题,可以阅读。

我也不清楚原因。不过,我清楚地知道,变量可以用x,也可以用别的符号,比如y,z,k,i,j...,甚至用alpha,beta这样的字母组合也可以。

变量在本质上就是一个占位符。这是一针见血的理解。

什么是占位符?就是先把那个位置用变量占上,表示这里有一个东西,至于这个位置放什么东西,以后再说,反正先用一个符号占着这个位置(占位符)。

其实在高级语言编程中,变量比我们在初中数学中学习的要复杂。但是,先不管那些,复杂东西放在以后再说了。现在,就按照初中数学的水平来研究Python中的变量。

通常使小写字母来命名Python中的变量,也可以是用下划线连接的多个单词。比如:alpha,x,j,p_beta,这些都可以做为Python的变量。

下面按照纯粹数学的方式,在Python中建立函数。

>>> a = 2>>> y = 3 * a + 2>>> y8

这种方式建立的函数,跟在初中数学中学习的没有什么区别。在纯粹数学中,也常这么用。这种方式在Python中还有效吗?

既然在上面已经建立了一个函数,那么我就改变变量a的值,看看得到什么结果。

>>> a = 3>>> y8

是不是很奇怪?为什么后面已经让a等于3了,结果y还是8。

还记得前面已经学习过的关于“变量赋值”的原理吗?a=2的含义是将2这个对象贴上了变量a标签,经过计算,得到了8,之后变量y引用了对象8。当变量a引用的对象修改为3的时候,但是y引用的对象还没有变,所以,还是8。再计算一次,y的连接对象就变了:

>>> a = 3>>> y8>>> y = 3 * a + 2>>> y11

特别注意,如果没有先 a = 2 ,就直接下函数表达式了,像这样,就会报错。

>>> y = 3 * a + 2Traceback (most recent call last):  File "<stdin>", line 1, in <module>  NameError: name 'a' is not defined

注意看错误提示,a是一个变量,提示中告诉我们这个变量没有定义。显然,如果函数中要使用某个变量,不得不提前定义出来。定义方法就是给这个变量赋值——这跟纯粹数学有所区别了。

用纯粹数学的方式建立函数,对Python不适用,如果非要找个根由,我想可能是“=”造成的,这个符号在数学中是等号,但是在Python中,包括所有的高级编程语言中,是“赋值”。这是我的肤浅理解。更深层的缘由,还在于计算机处理数据的原理与人不同。所以,要有一种新的定义函数的方式

定义函数

在Python中,规定了一种定义函数的格式,下面的举例就是一个函数,以这个函数为例来说明定义函数的格式和调用函数的方法。

#!/usr/bin/env python#coding:utf-8def add_function(a, b):    c = a + b    return cif __name__ == "__main__":    result = add_function(2, 3)    print result               #python3: print(result)

然后将文件保存,我把她命名为20101.py,你根据自己的喜好取个名字。

然后我就进入到那个文件夹,运行这个文件,出现下面的结果,如图:

你运行的结果是什么?如果没有得到上面的结果,你就非常认真地检查代码,是否跟我写的完全一样,注意,包括冒号和空格,都得一样。冒号和空格很重要。

下面开始庖丁解牛:

  • def: 这里是函数的开始。在声明要建立一个函数的时候,一定要使用def(def 就是英文define的前三个字母),意思就是告知计算机,这里要声明一个函数。def做在那一行,包括后面的add_function(a, b),被称为函数头。
  • add_function:这是函数名称。取名字是有讲究的,就好比你的名字一样。在Python中取名字的讲究就是要有一定意义,能够从名字中看出这个函数是用来干什么的。从add_function这个名字中,是不是看出她是用来计算加法的呢(严格地说,是把两个对象“相加”,这里相加的含义是比较宽泛的,包括对字符串等相加)?
  • (a,b):这是参数列表。要写在括号里面。这是一个变量(参数)列表,其中的变量(参数)指向函数的输入。在这个例子中,函数有两项输入,分别是ab。在通常的函数中,输入项没有限定,可以是任意数量,当然也可以没有输入,这时候的参数列表就是一对空着的圆括号(),但是,必须得有这个圆括号。
  • ::这个冒号非常非常重要,如果少了,就报错了。这和前面的语句是类似的,冒号表示函数头结束,下面要开始函数体的内容了。
  • c = a + b:这一行开始,就是函数体。函数体使一个缩进了四个空格的代码块,完成你需要完成的工作。在这个代码块中,可以使用函数头中的变量,当然,不使用也可以。缩进四个空格。这是Python的规定,要牢记,不可丢掉,丢了就报错。这句话就是将函数头的变量相加,结果赋值与另外一个变量c。
  • return c:还是提醒看官注意,缩进四个空格。return是函数的关键字,意思是要返回一个值。return语句执行时,Python跳出当前的函数并返回到调用这个函数的地方。在下面,有调用这个函数的地方result = add_function(2, 3)。但是,函数中的return语句也不是必须要写的,如果不写,Python将认为使以return None来作为结束的。也就是说,如果你的函数中没有return,事实上,在调用的时候,Python也会返回一个结果,这个结果就是None。
  • if __name__ == "__main__": 这句话先照抄,不解释,因为在《自省》有说明,不知道你是不是认真阅读了。注意就是不缩进了。
  • result = add_function(2, 3):这是调用前面建立的函数,并且传入两个值a=2b=3。仔细观察传入参数的方法,就是相当于把2放在a那个位置,3放在b那个位置(所以说,变量就是占位符)。当函数运行,遇到了return语句,就将函数中的结果返回到这里,赋值给result。还要啰嗦一句,是“相当于”把2和3分别放在a和b的位置,这个“相当于”的是有含义的,暂且存疑,后续会讲解。

解牛完毕,做个总结:

定义函数的格式为:

def 函数名(参数1,参数2,...,参数n):    函数体(语句块)

是不是样式很简单呢?

几点说明:

  • 函数名的命名规则要符合Python中的命名要求。一般用小写字母和单下划线、数字等组合,有人习惯用aaBb的样式,但我不推荐
  • def是定义函数的关键词,这个简写来自英文单词define
  • 函数名后面是圆括号,括号里面,可以有参数列表,也可以没有参数
  • 千万不要忘记了括号后面的冒号
  • 函数体(语句块),相对于def缩进,按照python习惯,缩进四个空格

看简单例子,深入理解上面的要点:

>>> def name():         #定义一个无参数的函数,只是通过这个函数打印...     print "qiwsir"  #缩进4个空格... >>> name()              #调用函数,打印结果qiwsir>>> def add(x,y):       #定义一个非常简单的函数...     return x+y      #缩进4个空格... >>> add(2,3)            #通过函数,计算2+35

注意上面的add(x,y)函数,在这个函数中,没有特别规定参数xy的类型。其实,这句话本身就是错的,还记得在前面已经多次提到,在Python中,变量无类型,只有对象才有类型,这句话应该说成:xy并没有严格规定其所引用的对象类型。这是Python跟某些语言比如java很大的区别,在有些语言中,需要在定义函数的时候告诉函数参数的数据类型。Python不用那样做。

为什么?列位不要忘记了,这里的所谓参数,跟前面说的变量,本质上是一回事。只有当用到该变量的时候,才建立变量与对象的引用关系,否则,关系不建立。而对象才有类型。那么,在add(x,y)函数中,x,y在引用对象之前,是完全飘忽的,没有被贴在任何一个对象上,换句话说它们有可能引用任何对象,只要后面的运算许可,如果后面的运算不许可,则会报错。

>>> add("qiw","sir")    #这里,x="qiw",y="sir",让函数计算x+y,也就是"qiw"+"sir"'qiwsir'>>> add("qiwsir",4)Traceback (most recent call last):  File "<stdin>", line 1, in <module>  File "<stdin>", line 2, in addTypeError: cannot concatenate 'str' and 'int' objects  #仔细阅读报错信息,就明白错误之处了

从实验结果中发现:x+y的意义完全取决于对象的类型。在Python中,将这种依赖关系,称之为多态。对于Python中的多态问题,以后还会遇到,这里仅仅以此例子显示一番。请看官要留心注意的:Python中为对象编写接口,而不是为数据类型。读者先留心一下这句话,或者记住它,随着学习的深入,会领悟到其真谛的。

此外,也可以将函数通过赋值语句,与某个变量建立引用关系:

>>> result = add(3, 4)>>> result7

在这里,其实解释了函数的一个秘密。add(x, y)在被运行之前,计算机内是不存在的,直到代码运行到这里的时候,在计算机中,就建立起来了一个对象,这就如同前面所学习过的字符串、列表等类型的对象一样,运行add(x,y)之后,也建立了一个add(x,y)的对象,这个对象与变量result可以建立引用关系,并且add(x,y)将运算结果返回。于是,通过result就可以查看运算结果。

>>> add<function add at 0x00000000007BAC80>

如果使用add(x, y)的样式,是调用那个函数。但是如果只写函数的名字,不写参数列表,就如同上面那样,我们得到的是该函数在内存汇总的存储信息。你还可以这样做:

>>> type(add)<class 'function'>      #Python 2下的反馈信息略有差异

这说明add是一个对象,因为只有对象才有类型,并且它是一个function类。按照我们的经验,对象都可以与一个变量建立引用关系,从而通过那个变量访问对象。

>>> r = add>>> r<function add at 0x00000000007BAC80>>>> r(3, 4)7>>> add(3, 4)7>>> type(r)<class 'function'>

通过赋值语句,变量r和函数对象建立了引用关系之后,就可以做所有add(x, y)能做的事情,因为r就是那个函数的代表。

刚开始接触函数,可能有点吃力。先放松一下,看看“名不正言不顺”的Python版。

关于命名

到现在为止,我们已经接触过变量的命名、函数的命名问题。似乎已经到了将命名问题进行总结的时候了。

在某国,向来重视“名”,所谓“名不正言不顺”,取名字或者给什么东西命名,常常是天大的事情,在很多时候就是为了那个“名”进行争斗。

江湖上还有的大师,会通过某个人的名字来预测他/她的吉凶祸福等。看来名字这玩意太重要了。“名不正,言不顺”,歪解:名字不正规化,就不顺。这是歪解,希望不要影响读者正确理解。不知道大师们是不是能够通过外国人名字预测外国人的吉凶祸福呢?比如Aoi sola,这个人怎么样?不管怎样,某国人是很在意名字的,旁边有个国家似乎就不在乎,比如山本五十六,在名字中间出现数字,就好像我们的张三李四王二麻子那样随便,不过,有一种说法,“山本五十六”的意思是这个人出生时,他父亲56岁,看来跟张三还不一样的。

Python也很在乎名字问题,其实,所有高级语言对名字都有要求。为什么呢?因为如果命名乱了,计算机就有点不知所措了。看Python对命名的一般要求。

  • 文件名:全小写,可使用下划线

  • 函数名:小写,可以用下划线风格单词以增加可读性。如:myfunction,my_example_function。注意:混合大小写仅被允许用于这种风格已经占据优势的时候,以便保持向后兼容。有的人,喜欢用这样的命名风格:myFunction,除了第一个单词首字母外,后面的单词首字母大写。这也是可以的,因为在某些语言中就习惯如此。但我不提倡,这是我非常鲜明的观点。

  • 函数的参数:命名方式同变量(本质上就是变量)。如果一个参数名称和Python保留的关键字冲突,通常使用一个后缀下划线会好于使用缩写或奇怪的拼写。

  • 变量:变量名全部小写,由下划线连接各个单词。如color = WHITE,this_is_a_variable = 1。

其实,关于命名的问题,还有不少争论呢?最典型的是所谓匈牙利命名法、驼峰命名等。如果你喜欢,可以google一下。以下内容供参考:

调用函数

前面的例子中已经有了一些关于调用的问题,为了深入理解,把这个问题单独拿出来看看。

为什么要写函数?从理论上说,不用函数,也能够编程,我们在前面已经写了程序,就没有写函数,当然,用Python的内建函数姑且不算了。现在之所以使用函数,主要是:

  1. 降低编程的难度,通常将一个复杂的大问题分解成一系列更简单的小问题,然后将小问题继续划分成更小的问题,当问题细化为足够简单时,就可以分而治之。为了实现这种分而治之的设想,就要通过编写函数,将各个小问题逐个击破,再集合起来,解决大的问题。(请注意,分而治之的思想是编程的一个重要思想,所谓“分治”方法也。)
  2. 代码重(chong,二声音)用。在编程的过程中,比较忌讳同样一段代码不断的重复,所以,可以定义一个函数,在程序的多个位置使用,也可以用于多个程序。当然,后面我们还会讲到“模块”(此前也涉及到了,就是import导入的那个东西),还可以把函数放到一个模块中供其他程序员使用。也可以使用其他程序员定义的函数(比如import ...,前面已经用到了,就是应用了别人——创造python的人——写好的函数)。这就避免了重复劳动,提供了工作效率。

这样看来,函数还是很必要的了。

废话少说,那就看函数怎么调用吧。以add(x,y)为例,前面已经演示了基本调用方式,此外,还可以这样:

Python2:

>>> def add(x,y):       #为了能够更明了显示参数赋值特点,重写此函数...     print "x=",x         #分别打印参数赋值结果...     print "y=",y...     return x+y... 

Python 3:

>>> def add(x, y):        print("x={}".format(x))        print("y={}".format(y))        return x+y>>> add(10, 3)           #x=10,y=3x= 10y= 313>>> add(3, 10)           #x=3,y=10x= 3y= 1013

所谓调用,最关键是要弄清楚如何给函数的参数赋值。这里就是按照参数次序赋值,根据参数的位置,值与之对应。

>>> add(x=10, y=3)       x= 10y= 313

还可以直接把赋值语句写到里面,就明确了参数和对象的关系。当然,这时候顺序就不重要了,也可以这样

>>> add(y=10, x=3)x= 3y= 1013

在定义函数的时候,参数可以像前面那样,等待被赋值,也可以定义的时候就赋给一个默认值。例如:

>>> def times(x, y=2):       #y的默认值为2...     print "x=",x                 #Python 3: print("x={}".format(x)),以下类似,从略。...     print "y=",y...     return x*y... >>> times(3)                #x=3,y=2x= 3y= 26>>> times(x=3)              #同上x= 3y= 26

如果不给那个有默认值的参数传递值(赋值的另外一种说法),那么它就是用默认的值。如果给它传一个,它就采用新赋给它的值。如下:

>>> times(3, 4)              #x=3,y=4,y的值不再是2x= 3y= 412>>> times("qiwsir")         #再次体现了多态特点x= qiwsiry= 2'qiwsirqiwsir'

请读者在闲暇之余用Python完成:写两个数的加、减、乘、除的函数,然后用这些函数,完成简单的计算。

在程序中调用函数,还需要注意一个貌似废话的事项,那就是“先定义,后使用”。说是废话,是因为在理解上似乎当然这样,但是,在实践中,常会遇到此类错误。

>>> def foo():    print('Hello, Teacher Cang!')        #Python 2的使用者请自动调整为print语句    bar()

这里定义了一个函数foo(),在这个函数里面还调用了一个函数bar(),但是这个bar()函数,此前并没有在什么地方定义。所以,如果调用foo()函数,就会这样:

>>> foo()Hello, Teacher Cang!Traceback (most recent call last):  File "<pyshell#44>", line 1, in <module>    foo()  File "<pyshell#43>", line 3, in foo    bar()NameError: name 'bar' is not defined

NameError:是一种错误信息。错误不可怕,可怕的是不认真看提示信息,只要耐心地认真地阅读提示信息,就能晓得错误原因。提示信息中分明告诉我们,那个bar没有定义。

所以要必须先定义,后使用。

>>> def bar(): pass

这就定义了bar(),虽然非常简短,函数体内的代码就一个pass,意思是里面什么也不做,统统地pass。然后调用foo()

>>> foo()Hello, Teacher Cang!

不再报错了。

虽然将bar()定义在了foo()的后面,只要定义了,无论先后,就可以使用。

注意事项

下面的若干条,是常见编写代码的注意事项:

  1. 别忘了冒号。一定要记住复合语句首行末尾输入“:”(if,while,for等的第一行)
  2. 从第一行开始。要确定顶层(无嵌套)程序代码从第一行开始。
  3. 空白行在交互模式提示符下很重要。模块文件中符合语句内的空白行常被忽视。但是,当你在交互模式提示符下输入代码时,空白行则是会结束语句。
  4. 缩进要一致。避免在块缩进中混合制表符和空格。
  5. 使用简洁的for循环,而不是while or range.相比,for循环更易写,运行起来也更快
  6. 要注意赋值语句中的可变对象。
  7. 不要期待在原处修改的函数会返回结果,比如list.append(),这在可修改的对象中特别注意
  8. 调用函数是,函数名后面一定要跟随着括号,有时候括号里面就是空空的,有时候里面放参数。
  9. 不要在导入和重载中使用扩展名或路径。

以上各点如果有不理解的,也不要紧,在以后编程中,时不时地回来复习一下,能不断领悟其内涵。