python入门系列10:函数1 港控/mmm° 2022-06-08 12:25 183阅读 0赞 # 函数概述 # # 函数的概念 # 函数就是把完成特定功能的一段代码封装起来。给该功能起一个名字(函数名)。 哪里需要实现该功能就在哪里调用该函数。 函数可以在任何时间任何地方调用。 -------------------- # 函数的作用 # * 函数是能完成某一功能的代码段 * 函数是可重复执行的代码段 * 函数方便管理和维护,便于复用 -------------------- # 定义函数 # 使 用函数之前一定要先定义。 python 的函数定义非常简单。 函数定义语法: def 函数名([形参列表]): 函数体 说明: 1. `def` 是定义函数的关键字。(define function) 1. 函数名是这个函数的符号(引用),调用这个函数的时候我们需要函数名。 2. 函数名后的圆括号是必须的。 3. 形参列表表示我们可以定义多个形参,接受函数调用时传递过来的参数。形参不是必须的,根据需要决定是否需要定义形参 4. 圆括号后面必须有一个分号`:`. 5. 新起一行,必须有缩进来定义函数体。函数体就是函数每次调用的时候都会执行的代码。 -------------------- # 定义无参函数 def show(): print("我是函数内的代码1") print("我是函数内的代码2") # 定义有参函数,形参在函数内部可以作为普通变量使用。 def show1(a, b): print(a + b) # 函数调用 # 定 义函数的目的是为了让函数做一些事情。 但是函数如果仅仅定义不会自己去执行。 **时刻记住一句话,函数只有被调用才能被执行!** 所以想要函数执行,必须显示的去调用函数。 **在使用函数的时候,一定要保证声明在前,调用在后!** -------------------- # 简单函数调用 # 函数调用非常简单,只需要: `函数名(实参)`即可。当然如果函数定义的时候没有形参,则就不用实参。 def show(): print("我是函数内的代码1") print("我是函数内的代码2") show() # 调用函数 show() # 一个函数可以多次调用。每次调用都会执行一次函数体的代码 show() ![90756090.jpg-yztcText][] -------------------- # 带形参的函数调用 # 函数声明的时候,在括号内的是形参。 那么在调用函数的时候应该传递相同属性的参数过去,函数调用的时候的参数,叫你实参! 形参和实参的个数必须匹配,但是有一种情况除外,后面再说。 def add(a, b): print(a + b) add(2, 3) add(20, 3) ![21505592.jpg-yztcText][] -------------------- # 函数的返回值 # \# 4.1.`return`的使用 前面定义的函数都是非常简单的,仅仅做了一些输出。在实际开发中,这种函数其实没有太多的用处。 返回值在函数定义中一个非常重要的地方。 我们定义的函数可以完成一个的功能,很多情况下,功能完成之后需要给函数调用者返回一些数据,这些返回的数据就需要用到函数的返回值功能。 -------------------- 比如:定义一个函数可以实现两个数的相加,然后返回给调用者计算的结果! def add(a, b): n = a + b return n print(add(3, 5)) print(add(30, 5)) ![72992946.jpg-yztcText][] 说明: 1. 在函数内任何地方都可以出现`return`。`return`的作用就是结束函数,并把`return`后面的值返回给调用者。 1. 一旦碰到 `return`,不管后面有多少代码,不管是否处于循环中,函数都会立即结束。 2. 如果整个函数内部没有出现`return`,则函数会自动执行到函数体最后一行代码。 3. 如果函数体内没有`return`,则函数运行结束的时候自动返回`None` 4. `return`后面也可以不跟返回值,这种情况下主要是为了结束函数,也会返回`None` -------------------- ## 案例1:一个给定的数,判断是否为质数 ## 分析:定义一个函数,这个给定的数通过参数传递,最后结果:是否为质数,通过返回一个`bool`值给调用者。 def is_prime(num): for i in range(2, num): if num % i == 0: return False return True num = int(input("请输入一个整数:")) if is_prime(num): print("%d 是质数!" % num) else: print("%d 不是是质数!" % num) -------------------- ## 案例2:计算输入的任意两个数之间所有的质数的和 ## 分析:刚才已经定义了判断一个是否为质数,现在需要再定义一个可以计算两个数之间所有的质数的和的函数,并把计算结果返回给调用者。 def is_prime(num): for i in range(2, num): if num % i == 0: return False return True def add_prime(num1, num2): sum = 0 for num in range(num1, num2): if is_prime(num): sum += num return sum num1 = int(input("请输入第一个整数:")) num2 = int(input("请输入第2个整数:")) print("%d 到 %d 的之间所有的质数的和是:%d" % (num1, num2, add_prime(num1, num2))) -------------------- # 4.2.返回多个值 # 使用`return`一次只能返回一个值。 有些场景下,我们需要返回多个值。这种情况下我们我们可以把返回值的封装到`list`或`tuple`中,接受者拿到这个`list`或`tuple`直接解包就可以使用了。 最好封装在`tuple`中,因为`tuple`是不可变的, 所以效率比较高。 def foo(x): return x ** 2, x ** 3, x ** 4 x = 4 a, b, c = foo(x) print("%d 的平方是:%d, 3次方式:%d, 4次方是:%d" % (x, a, b, c)) ![1499389236855.png-yztcText][] -------------------- # 文档注释 # 以 前我们使用 `#` 来我们的代码添加注释,只使用单行注释。 我们定义了一个好函数,函数的描述等信息也应该让调用者很容易获取到,这个时候就用到文档注释。 文档注释将来可以直接生成 `api` 文档方便阅读。 如果要给函数添加文档注释,直接在函数内部的首行放置一个字符串即可。字符串可以是单行的(`" "或者' '`),也可以是是多行的(`""" """ 或 ''' '''`)。 **但是一般使用多行字符串来定义** 作为国际惯例: 1. 注释的第一行,一般是对函数的简述。 2. 然后一个空行。 3. 然后开始进行详细描述函数功能等。 def foo(x): """该函数是对参数做一些计算 计算参数的多个次幂 :param x: 要计算次幂的数字 :return: 返回2次幂、3次幂、4次幂组成的元组 """ return x ** 2, x ** 3, x ** 4 print(foo.__doc__) # 打印函数的文档注释 ![1499392012008.png-yztcText][] -------------------- # 作用域规则 # 有了函数之后,我们必须要面对一个作用域的问题。 比如:你现在访问一个变量,那么 python 解析器是怎么查找到这个变量,并读取到这个变量的值的呢? 依靠的就是作用域规则! -------------------- \# 命名空间(`namespace`) **先了解第一个概念:命名空间。** 命名空间是从*命名到对象的映射*。当前 pytyon 的命名空间是靠字典来实现的,但是对我们来说我们并需要关注这个,而且以后也有可能会更改。 -------------------- 命名空间的一些例子: 1. 内置的命名集(`the set of built-in names`)。这命名集(命名空间)包含了像`abs()`这样的内置函数和内置的异常名等。 1. 在一个模块中的全局命名集。 2. 函数内的局部命名空间。 3. 从某种意义上来说,一个对象的属性集也是一个命名空间。 **对命名空间来说,需要要知道最重要的一点是:在不同的命名空间的中的命名绝对没有任何的关系!** -------------------- **不同的命名空间具有不同的生命周期。** 1. 包含内置命名的命名空间在 python 一启动就被创建了,而且在 python 程序结束之前永远不会被删除。 2. 一个模块的全局命名空间是在这个模块被读入到内存的时候被创建。正常情况下全局命名空间也是在 python 结束之后才会被删除。 3. 局部命名空间是在函数被调用的时候被创建。当函数结束或者抛了异常这个函数又没处理的时候,局部命名空间会被销毁。(用忘记这个局部命名空间可能更恰当) 4. 当然,函数递归调用时,每调用一次都会创建一个新的局部命名空间。 -------------------- \# 作用域(`scope`) 作用域就是 python 程序的一块文本区域,在这个区域内,可以直接访问(Directly accessible)命名空间。 直接访问的意思就是:当你访问一个绝对的命名的时候,直接在命名空间中查找 尽管作用域的定义是静态的,但是作用域的使用(查找变量)却是动态的。 -------------------- 在代码执行的任何时间,至少有 3 个嵌套的作用域,这些作用域的命名空间可以直接访问。 1. 内部作用域(局部作用域)。包含了所有的局部命名,在访问变量的时候,首先在内部作用域中查找。 2. 然后是嵌套函数的外层作用域。在这里搜索非局部,但也是非全局的命名。(在 python 中允许在函数中定义函数的) 3. 然后是包含当前模块的全局作用域。 4. 最后搜索的是最外层的创建内置命名的作用域。 -------------------- \# 作用域在 python 中的具体应用 ## 6.3.1.访问局部作用域 ## def foo(): a = 20 print(a) foo() 说明: 函数内部访问变量`a`, 先在`foo`函数内部查找。因为 `a`确实是在函数内部声明的变量,然后就找到了`a` -------------------- ## 6.3.2.访问外部作用域 ## a = 100 def foo(): print(a) foo() 说明: 1. 在`foo`函数内部,我们直接去访问一个变量 `a`,那么就会沿着作用域从内向外开始查找`a` 2. 先查找`foo`的局部作用域,发现没有`a`。然后继续去`foo`函数的外部作用域,这个例子中就直接到了当前模块的全局作用域,所以找到了 a, 所以就输出了全局作用域中`a`的值! -------------------- ## 6.3.3.访问外部函数的作用域 ## def outer(): a = 20 def inner(): print(a) inner() outer() 说明: 1. 我们在一个函数的内部声明了一函数,这种函数嵌套在 python 中是允许的。 2. 内部函数`inner`执行的时候,访问变量`a`,现在`inner`内部找变量`a`, 没有找到,然后去他外部的函数中找变量`a`, 找到后, 就直接输出了他的值 -------------------- \# python 针对修改变量值的特殊情况 ## 6.4.1.只能修改局部变量 ## **在 python 的函数中, 修改一个变量的值的时候,永远操作的是局部变量** 为什么会这样呢? 这其实是由 python 定义变量的方式所决定的. python 不需要显示的去定义变量,直接赋值的时候如果变量不存在直接就定义了. 如果在函数内部可以直接修改外部作用域变量的值,则就无法定义一个同名变量了. 所以, python 才规定不能在函数内部直接修改外部作用域变量的值. -------------------- a = 10 def foo(): a = 20 # 这里其实是新创建了一个局部变量 a .并不是修改的全局作用域的变量 a print(a) # 根据作用域的查找规则,这里访问的是局部变量 a foo() print(a) # 根据作用域查找规则,这里访问的是全局作用域的 a ![1499409634962.png-yztcText][] -------------------- ## 6.4.2.变量必须先赋值才能使用 ## 看下面的代码: a = 10 def foo(): a = a + 2 foo() ![1499410335508.png-yztcText][] **说明:** `a = a + 2` 这行代码有问题. 为什么? 首先要搞清楚 `a + 2` 中的`a`是局部变量还是全局变量? **是局部变量`a`!** 在解释器运行这个函数的时候, 已经检测到函数内部有创建局部变量`a`, 所以这个时候你访问到的一定是局部变量`a`. `a + 2` 中的局部变量`a`还没有 被赋值,所以和 2 相加抛出了异常.`UnboundLocalError` -------------------- a = 10 def foo(): print(a) a = 20 foo() ![1499410813890.png-yztcText][] 说明: 原因和前面的一样的. 解析器已经检测到你后面会声明局部变量`a`, 所以`print(a)`中的 `a` 仍然是局部变量.但是还没有赋值,所以就抛异常了 -------------------- **总结:** 在函数内部如果你定义了局部变量,那么你在任何地方都没有办法访问到函数外部作用域的同名变量. -------------------- \# 函数内修改全局变量 通过前面的学习, 正常情况下我们知道了在函数内部没有办法修改全局变量的值! **但是这只是正常情况下!** 如果我们有在函数内部修改全局变量值的需求怎么办? ***也是可以的, 但是我们需要做些小动作: 使用关键字`global`*** -------------------- a = 10 def foo(): global a # 告诉 pytyon 解析器, a 以后就是全局变量了 a = 20 foo() print(a) # 20 **说明:** 1. `global` 后面跟上全局变量的名, 那么在后面的代码中就可以使用全局变量了. 2. 如果有多个全局变量需要修改, `global`可以同时定义多个全局变量.`global a, b, c` -------------------- \# 内部函数修改外部函数的局部变量 当用到函数嵌套的时候, 内部函数正常情况下也是无法修改外部函数的局部变量, 只能访问读取. 如果想修改怎么办? 使用关键字:`nonlocal` a = 10 def outer(): a = 20 def inner(): nonlocal a # 把 a 绑定到外部函数的局部变量 a 上 a = 30 inner() print("outer 的局部变量a:" + str(a)) # 30 被内部函数 inner 修改了 outer() print("全局变量a:" + str(a)) ![1499413643425.png-yztcText][] -------------------- [90756090.jpg-yztcText]: http://o7cqr8cfk.bkt.clouddn.com/17-7-6/90756090.jpg-yztcText [21505592.jpg-yztcText]: http://o7cqr8cfk.bkt.clouddn.com/17-7-6/21505592.jpg-yztcText [72992946.jpg-yztcText]: http://o7cqr8cfk.bkt.clouddn.com/17-7-6/72992946.jpg-yztcText [1499389236855.png-yztcText]: http://o7cqr8cfk.bkt.clouddn.com/markdown/1499389236855.png-yztcText [1499392012008.png-yztcText]: http://o7cqr8cfk.bkt.clouddn.com/markdown/1499392012008.png-yztcText [1499409634962.png-yztcText]: http://o7cqr8cfk.bkt.clouddn.com/markdown/1499409634962.png-yztcText [1499410335508.png-yztcText]: http://o7cqr8cfk.bkt.clouddn.com/markdown/1499410335508.png-yztcText [1499410813890.png-yztcText]: http://o7cqr8cfk.bkt.clouddn.com/markdown/1499410813890.png-yztcText [1499413643425.png-yztcText]: http://o7cqr8cfk.bkt.clouddn.com/markdown/1499413643425.png-yztcText
还没有评论,来说两句吧...