如何理解函数(1)

函数(1)

(本文抄写于老齐python教程)

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

下面一段来自维基百科(本教程中,大量来源于维基百科)

函数这个数学名词,是莱布尼兹唉1694开始使用的.以描述一个曲线的相关量.如曲线的斜率或曲线上的
一个点.莱布尼兹所指的函数现可以称为可导函数.数学家之外的普通人一般接触到的函数即属于该类.
对于可导函数可以讨论它的极限和导数.此两者描述了函数输出值的变化同输入值变化的关系,是微积分的基础.
中文的函数一词由清朝数学家李善兰翻译,其<代数>书中解释:凡此函数中彼变数者,则可称之为函数

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

def

理解函数

在中学数学中,可以这样定义函数,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.

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

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

通常小写字母来命名python中的变量,可以下划线连接多个单词,u:alpha, x, j ,p_beta ,这些都可以作为python的变量

下面是纯粹的数学方式,在python中建立函数,

1
2
3
a = 2
y = 3 * a +2
y

这种方式建立的函数,跟在初中数学中的学习没有任何区别,在纯粹数学中,也常这么用.这种方式在python中还有效嘛?

既然在上面已建立一个函数,改变变量a的值,那么得到什么结果.

1
2
3
4

a= 3
y
8

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

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

1
2
3
4
5
6
a = 3
y
8
y = 3 * a +2
y
11

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

1
2
3
4
5
y = 3 * a +2

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

注意看错误提示, a 是一个变量, 提示告诉我们变量没有定义.显然,如果函数中使用某个变量,不得不提前定义出来,定义方法就是给这个变量赋值.

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

定义函数

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

1
2
3
4
5
6
7
8
9
10
#! /usr/bin/env python
#coding:utf-8

def add_function(a,b):
c =a + b
return c

if __name__ = "__main__":
result = add_function(2,3)
print(result)

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

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

error

下面开始庖丁解牛:

  • def: 这里是函数的开始,在声明要建立一个函数的时候,一定要是使用def ,意思就是告诉计算机,这里要声明一个函数,def 做在那一行, 包括后面的add_function(a,b),被称为函数头.

  • add_function: 这是函数名称,取名字是有讲究的,就好比你的名字一样,在python中取名的讲究也是一定意义.能够从函数中看出这个函数是用来干什么的. 从add_function这个名字中,是不是看出她hi用来计算加法的呢?

  • (a,b): 这是参数列表,要写成括号里,这是一个变量(参数)列表,其中的变量(参数)指向函数的输入.在这个例子中,函数有两项输入.分别a和b. 在通常的函数中,输入项没有限定,可以是任意数量,当然也可以没有输入. 这时候的参数列表就是一对(). 但是,必须有这个圆括号.

  • : , 这个冒号也非常重要,如果少了,就会报错,这是和前面的语句类似,冒号表示函数头结束.

  • c = a + b ,这一行开始,这就是函数体.函数体使一个缩进了四个空格的代码快.完成你所需要的工作,在这个代码中,可以使用函数头中的变量,当然,不使用也可以. 缩进四个空格,这是PYTHON的规定.要牢记,这句话是将函数头的变量相加.

  • return c : 还是提醒注意,缩进四个空格. return语句执行时,python会跳出当前的函数并返回到调用这个函数的地方.在下面,有调用这个函数的地方 result = add_function(2,3) .但是,函数中的return 也不是必须要写的.如果不写,python将认为以return None作为结束的.如果你的函数没有None,事实上,在调用的时候,python 也会返回一个结果. 这个结果就是None.

  • if name = ‘main‘ : 这句话先不解释.

  • result = add_function(2,3) : 这是调用前面建立的函数,并且传入值 a =2 和 b=3 ,仔细观察传入参数的方法, 就是相当于2放在a那个位置,3放在b 那个位置 . 当函数运行时,遇到了return语句,u见过函数中的结果返回到这里.赋值给reuslt .这里需要注意的是, 相当于,

解牛完毕: 做个总结

1
2
3

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

是不是样式很简单呢?

  • 函数名的命名规则要符合python命名要求.一般都用小写字母,单下划线,数字组合.
  • def 是 定义函数的关键词,这个简写来自英文单词define
  • 函数名后面是圆括号,括号后面,可以有参数列表,也可以没有参数.
  • 千万不要忘记括号后面的冒号
  • 函数体(语句块) ,相对于def缩进,按照python习惯,缩进四个空格.
1
2
3
4
5
6
7
def name():
print(qwh)

name()

def add(x,y):
return x+y

注意上面的add(x,y)函数,在这个函数中,没有特别规定参数x,y的类型,其实这句话就是错的,还记得前面已经多次提到,在python中,变量无类型,只有对象才有类型,这句话应该说成,x,y并没有严格规定其所引用的对象类型,这是python跟JAVA很大的区别.

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

1
2
3
4
5
6
7
8
9

add ('q','b')
qb

add('x',2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in add
TypeError: cannot concatenate 'str' and 'int' objects

从实验结果看,x+y的意义完全取决于对象的类型. 在python中,将这种依赖关系,称之为多态. python为对象编写接口,而不是为了数据类型.

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

1
2
result = add(3,4)
result

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

1
2
add
<function add at 0x00000000007BAC80>

如果使用add(x,y)的样式,是调用这个函数,如果只写函数的名字,不写参数列表,我们得到的是,函数在内存汇总的储蓄信息.

``Python

type(add)


1
2
3
4
5
6

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

```Python
r = add
r(3,4)

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

关于命名

到现在为止,我们已经接触过变量的命名, 函数的命名问题.

  • 文件名:全小写,可使用下划线.
  • 函数名: 小写,可以用下划线增加可读性.
  • 函数的参数:命名方式同变量
  • 变量: 变量名全部小写.

调用函数

为什么要写函数? 从理论上讲,不用函数,也能够编程, 之所以使用函数,主要是:

  1. 降低编程难度. 通常将一个复杂的大问题,分解成一个系列更简单的小问题,然后将小问题继续划分成更小的问题. 当问题细化为足够简单的时候, 就可以分而治之.
  2. 代码重用.
1
2
3
4
deg add(x,y):
print("x={}".format(x))
print("y={}".format(y))
return x+y

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

1
add(x=10,y=3)

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

1
add(y=3,x=10)

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

1
2
3
4
5
6
7
8
9
def times(x,y=2):
print("x={}".format(x))
print("y={}".format(y))
return x * y

times(3)
x=3
y=2
6

如果不给那个默认值的参数传递值,那么它就是使用默认的值.如果给它传一个,他就采用新赋给他的值.

1
2
3
4
5
6
times(3,4)
12

times('abc')

abcabc

注意,上面引用了一个字符串, 证明了python的多态性, 回想起来函数的对象引用同样是这个道理.

在程序中调用函数, 需要注意”先定义,后使用”

1
2
3
4

def foo():
print('hello world')
bar()

如果调用foo(),就会报错.所以必须先定义.

1
2
3
4
5



def bar():
pass

bar()定义在foo后面,只要是定义了, 无关先后,就可以使用.

注意事项: