当前位置:   article > 正文

python声明函数后边定义的函数_Python--不能不知道的自定义函数

python 函数定义在后面

68f0009714f9b8130873f96f8585e6e5.png

我们都知道一个规范的Python程序,除非代码量太少,否则都应该让程序由多个函数组成,这样的代码才更加的规模化、模块化。

Python本身自带很多的内置函数,例如open()、len()、int()等等,太多了,但即使有大量的内置函数,仍然需要我们自己根据具体的场景,自定义某个函数。

函数基础

函数说白了就是为了实现某一个功能的代码块,写好之后就可以复用。先看一段代码:

  1. def my_func(message):
  2. print('Got a message: {}'.format(message))
  3. # 调用函数 my_func()
  4. my_func('Hello World')
  5. # 输出
  6. Got a message: Hello World
  • def 是函数的声明;
  • my_func 是函数的名称;
  • 括号里的message是函数的参数;
  • print输出这一行是函数的主体部分,也就是执行的语句;
  • 在函数的最后可以调用结果(return或yield),也可以不返回。

所以最后就是下面这种形式:

  1. def name(param1, param2, ..., paramN):
  2. statements
  3. return/yield value # optional

def是可执行语句,这意味着函数直到被调用前,都是不存在的,当程序调用函数时,def语句才会创建一个新的函数对象,并赋予其名字。

再看个例子:

  1. def my_sum(a, b):
  2. return a + b
  3. result = my_sum(3, 5)
  4. print(result)
  5. # 输出
  6. 8

这里定义了一个名为my_sum的函数,有a,b两个参数,函数返回的是a+b这个结果,例如传参3和5,最后结果为8。

另外Python函数的参数可以设定默认值,比如:

  1. def func(param = 0):
  2. ...

这样这个函数,在调用func()这个函数时,如果没有给param传参,则参数的默认值就为0,如果给param传了参数,则会覆盖默认值,使用的是传入的参数。

我们都知道Python是动态类型的语言,它可以接受任何的数据类型(整型,浮点型,字符串等等),对函数的参数而言,也是适用的,比如刚刚的my_sum函数,我们可以计算两个数字相加,也可以把列表或字符串作为参数来传递:

  1. print(my_sum([1, 2], [3, 4]))
  2. # 输出
  3. [1, 2, 3, 4]
  4. print(my_sum('hello ', 'world'))
  5. # 输出
  6. hello world

当然传递的两个参数一定要类型相同,否则一个数字,一个字符串肯定是要报错的。

所以这就是Python的多态行为。在实际运用中,这种特性会让Python非常方便,但同时可能会有诸多问题,所以最好在运用前,把数据类型检查好。

Python函数的另一大特性就是支持函数的嵌套,就是函数里边又有函数:

  1. def f1():
  2. print('hello')
  3. def f2():
  4. print('world')
  5. f2()
  6. f1()
  7. # 输出
  8. hello
  9. world

这里是f1()函数内部又定义了f2()函数,所以在调用f1()函数时,会先打印字符串‘hello’,再在f1()函数内部调用f2(),打印出字符串‘world’。

话说回来,函数的嵌套有什么用处?

第一,函数的嵌套可以保证内部函数的隐私,因为内部函数只能被外部函数调用和访问,不会暴露全局的作用域,比方说函数内部有一些隐私数据(数据库的用户和密码等),你不想暴露在外,就可以使用函数嵌套这种方式,把它封装到函数的内部,比如:

  1. def connect_DB():
  2. def get_DB_configuration():
  3. ...
  4. return host, username, password
  5. conn = connector.connect(get_DB_configuration())
  6. return conn

这里get_DB_configuration()函数就是内部函数,它不能在connect_DB()这个函数以外调用,直接调用就会报错:

  1. get_DB_configuration()
  2. # 输出
  3. NameError: name 'get_DB_configuration' is not defined

这样的话,我们只能靠外部的函数来访问内部的函数,程序的安全性便有了提高。

第二,合理使用嵌套函数,能够提升程序的运行效率,比如:

  1. def factorial(input):
  2. # validation check
  3. if not isinstance(input, int):
  4. raise Exception('input must be an integer.')
  5. if input < 0:
  6. raise Exception('input must be greater or equal to 0' )
  7. ...
  8. def inner_factorial(input):
  9. if input <= 1:
  10. return 1
  11. return input * inner_factorial(input-1)
  12. return inner_factorial(input)
  13. print(factorial(5))

这里是使用递归的方式计算一个数的阶乘,因为在计算之前,需要检查输入的合法性,所以如果写成嵌套的形式,输入的合法性只需要检查一次,如果不使用嵌套的话,那每调用一次递归就会检查一次,这显然是没有必要的,降低程序的运行效率。

函数变量的作用域

Python函数变量的作用域跟其他语言差不多,如果变量是在函数内部定义的,那么就只有在函数内部有效,被称为局部变量。一旦函数执行完毕后,变量就会被Python回收。

相应的全局变量则是定义在整个文件层次上的,比如:

  1. MIN_VALUE = 1
  2. MAX_VALUE = 10
  3. def validation_check(value):
  4. if value < MIN_VALUE or value > MAX_VALUE:
  5. raise Exception('validation check fails')

这里的MIN_VALUE,MAX_VALUE就是全局变量,可以在文件内的任何地方被访问,函数的内部也可以,但是,我们不能在函数内部随意更改全局变量的值。例如下面:

  1. MIN_VALUE = 1
  2. MAX_VALUE = 10
  3. def validation_check(value):
  4. ...
  5. MIN_VALUE += 1
  6. ...
  7. validation_check(5)
  8. ##程序报错信息
  9. UnboundLocalError: local variable 'MIN_VALUE' referenced before assignment

这是因为Python的解释器会默认把函数内部的变量作为局部变量,也就是说,全局变量MIN_VALUE 和MAX_VALUE在没有在函数内部被声明的情况下,在函数内部被访问就会当成是局部变量,所以,如果想要改变全局变量MIN_VALUE 和MAX_VALUE的值,就要加上global这个声明:

  1. MIN_VALUE = 1
  2. MAX_VALUE = 10
  3. def validation_check(value):
  4. global MIN_VALUE
  5. ...
  6. MIN_VALUE += 1
  7. ...
  8. validation_check(5)

这个global关键字不是创建一个名为MIN_VALUE 和MIN_VALUE的全局变量 ,而是告诉Python解释器这个函数内部被访问的MIN_VALUE 和MAX_VALUE就是之前定义的全局变量,不是新的全局变量,也不是局部变量。这样声明之后,我们就可以在函数内部去访问并修改它的值了。

类似的,对于嵌套函数来说,内部函数可以访问外部函数的变量,但是无法修改,若要修改需加上nonlocal这个关键字:

  1. def outer():
  2. x = "local"
  3. def inner():
  4. nonlocal x # nonlocal 关键字表示这里的 x 就是外部函数 outer 定义的变量 x
  5. x = 'nonlocal'
  6. print("inner:", x)
  7. inner()
  8. print("outer:", x)
  9. outer()
  10. # 输出
  11. inner: nonlocal
  12. outer: nonlocal

还有如果不加上nonlocal这个关键字,内部函数的变量和外部函数的变量同名,则内部函数的变量会覆盖外部函数的变量。

  1. def outer():
  2. x = "local"
  3. def inner():
  4. x = 'nonlocal' # 这里的 x 是 inner 这个函数的局部变量
  5. print("inner:", x)
  6. inner()
  7. print("outer:", x)
  8. outer()
  9. # 输出
  10. inner: nonlocal
  11. outer: local

闭包

啥是闭包尼?其实跟嵌套函数很类似,不一样的就是,外部函数不是返回的一个具体的值,而是返回的是一个函数,返回的函数通常赋予了变量,这个变量可以在后面被继续调用。

  1. def nth_power(exponent):
  2. def exponent_of(base):
  3. return base ** exponent
  4. return exponent_of # 返回值是 exponent_of 函数
  5. square = nth_power(2) # 计算一个数的平方
  6. cube = nth_power(3) # 计算一个数的立方
  7. square
  8. # 输出
  9. <function __main__.nth_power.<locals>.exponent(base)>
  10. cube
  11. # 输出
  12. <function __main__.nth_power.<locals>.exponent(base)>
  13. print(square(2)) # 计算 2 的平方
  14. print(cube(2)) # 计算 2 的立方
  15. # 输出
  16. 4 # 2^2
  17. 8 # 2^3

例如上边这个计算一个数的n次幂的例子,外部函数nth_power()的返回值是函数exponent_of(),在执行完square = nth_power(2)和cube = nth_power(3)后,外部函数nth_power()的参数exponent,仍然会被内部函数exponent_of()记住,这样在调用square(2)和cube(2)时,程序就可以输出相应的结果。

所以,又有疑问了?问啥要用闭包,明明写一个普通的函数也可以啊,例如下面这种形式:

  1. def nth_power_rewrite(base, exponent):
  2. return base ** exponent

像计算一个数的平方这么简单的例子,确实用不用闭包都无所谓,旦所谓量变会产生质变,一旦计算本身的复杂度增加或计算数量的增加,你就不得不考虑计算效率的问题了,看下面你觉得哪个更加简洁且效率更高:

  1. # 不适用闭包
  2. res1 = nth_power_rewrite(base1, 2)
  3. res2 = nth_power_rewrite(base2, 2)
  4. res3 = nth_power_rewrite(base3, 2)
  5. ...
  6. # 使用闭包
  7. square = nth_power(2)
  8. res1 = square(base1)
  9. res2 = square(base2)
  10. res3 = square(base3)
  11. ...

肯定是第二种,所以闭包的有点可嵌套函数类似,比方说在函数的开头需要做一下额外的工作,但这个工作我只需要做一次就够了,当我用普通函数定义时,我每调用一次函数,这个额外的工作便会重新工作一次,那如果我把这些额外的工作放在外部函数里,就可以有效的减少因为多次调用而浪费不必要的计算开销。

其实闭包还经常跟装饰器一起使用,但装饰器的使用以后再讲,所以这一点就暂时不提了。

所以,Python函数的自定义函数你了解的差不多了吗?

6d93ce2f590a3b44eb502529adf1357d.png
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/616817
推荐阅读
相关标签
  

闽ICP备14008679号