• 【Python 3】函数


    有了函数,我们就不再每次写s = 3.14 * x * x,而是写成更有意义的函数调用s = area_of_circle(x),而函数area_of_circle本身只需要写一次,就可以多次调用

    Python不但能非常灵活地定义函数,而且本身内置了很多有用的函数,可以直接调用

    要调用一个函数,需要知道函数的名称和参数,比如求绝对值的函数abs,只有一个参数
    可以直接从Python的官方网站查看文档
    http://docs.python.org/3/library/functions.html#abs
    也可以在交互式命令行通过help(abs)查看abs函数的帮助信息

    调用函数的时候,如果传入的参数数量不对,会报TypeError的错误
    如果传入的参数数量是对的,但参数类型不能被函数所接受,也会报TypeError的错误


    Python内置的常用函数还包括数据类型转换函数,比如int()函数可以把其他数据类型转换为整数

    int('123') # 123
    int(12.34) # 12
    float('12.34') # 12.34
    str(1.23) # '1.23'
    str(100) # '100'
    bool(1) # true
    bool('') # False
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个别名

    在Python中,定义一个函数要使用def语句,依次写出函数名、括号、括号中的参数和冒号:
    然后,在缩进块中编写函数体,函数的返回值用return语句返回

    如果你已经把my_abs()的函数定义保存为abstest.py文件了,那么,可以在该文件的当前目录下启动Python解释器,用from abstest import my_abs来导入my_abs()函数,注意abstest是文件名(不含.py扩展名)

    pass语句什么都不做,那有什么用?实际上pass可以用来作为占位符,比如现在还没想好怎么写函数的代码,就可以先放一个pass,让代码能运行起来

    修改一下my_abs的定义,对参数类型做检查,只允许整数和浮点数类型的参数。数据类型检查可以用内置函数isinstance()实现

    def my_abs(x):
    	if not isinstance(x, (int, float)):
    		raise TypeError('bad operand type')
    	if x >= 0:
    		return x
    	else:
    		return -x
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    import math
    
    def move(x, y, step, angle=0):
    	nx = x + step * math.cos(angle)
    	ny = y - step * math.sini(angle)
    	return nx, ny
    
    x, y = move(100, 100, 60, math.pi / 6)
    print(x, y)
    # 151.96152422706632 70.0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    import math语句表示导入math包,并允许后续代码引用math包里的sin、cos等函数

    原来返回值是一个tuple
    但是在语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值,所以,Python的函数返回多值其实就是返回一个tuple,但写起来更方便


    定义函数的时候,我们把参数的名字和位置确定下来,函数的接口定义就完成了

    对于函数的调用者来说,只需要知道如何传递正确的参数,以及函数将返回什么样的值就够了,函数内部的复杂逻辑被封装起来,调用者无需了解

    Python的函数定义非常简单,但灵活度却非常大
    除了正常定义的必选参数外,还可以使用默认参数可变参数关键字参数,使得函数定义出来的接口,不但能处理复杂的参数,还可以简化调用者的代码

    def power(x, n):
    	s = 1
    	while n > 0:
    		n -= 1
    		s = s * x
    	return s
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    修改后的power(x, n)函数有两个参数:x和n,这两个参数都是位置参数,调用函数时,传入的两个值按照位置顺序依次赋给参数x和n

    由于我们经常计算x2,所以,完全可以把第二个参数n的默认值设定为2

    def power(x, n=2):
    	s = 1
    	while n > 0:
    		n -= 1
    		s = s * x
    	return s
    
    >>> power(5)
    25
    >>> power(5, 2)
    25
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    从上面的例子可以看出,默认参数可以简化函数的调用
    设置默认参数时,有几点要注意

    1. 必选参数在前,默认参数在后
    2. 把变化大的参数放前面,变化小的参数放后面

    定义默认参数要牢记一点:默认参数必须指向不变对象!

    为什么要设计str、None这样的不变对象呢
    因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误
    此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有
    我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象


    在Python函数中,还可以定义可变参数

    def calc(*numbers):
    	sum = 0
    	for n in numbers:
    		sum = sum + n * n
    	return sum
    
    >>> calc(1, 2, 3)
    14
    >>> calc(1, 3, 5, 7)
    84
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    定义可变参数和定义一个list或tuple参数相比,仅仅在参数前面加了一个*号
    在函数内部,参数numbers接收到的是一个tuple,因此,函数代码完全不变
    但是,调用该函数时,可以传入任意个参数,包括0个参数

    Python同时也允许在list或tuple前面加一个*号,把list或tuple的元素变成可变参数传进

    nums = [1, 2, 3]
    calc(*nums) # 14
    
    • 1
    • 2

    *nums表示把nums这个list的所有元素作为可变参数传进去
    这种写法相当有用,而且很常见


    可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple
    关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict

    def person(name, age, **kw):
    	print('name:', name, 'age:', age, 'other:', kw)
    
    • 1
    • 2

    函数person除了必选参数name和age外,还接受关键字参数kw。在调用该函数时,可以只传入必选参数
    也可以传入任意个数的关键字参数

    >>> person('Michael', 30)
    name: Michael age: 30 other: {}
    
    >>> person('Bob', 35, city='Beijing')
    name: Bob age: 35 other: {'city': 'Beijing'}
    >>> person('Adam', 45, gender='M', job='Engineer')
    name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    关键字参数有什么用?它可以扩展函数的功能
    比如,在person函数里,我们保证能接收到name和age这两个参数,但是,如果调用者愿意提供更多的参数,我们也能收到。试想你正在做一个用户注册的功能,除了用户名和年龄是必填项外,其他都是可选项,利用关键字参数来定义这个函数就能满足注册的需求

    和可变参数类似,也可以先组装出一个dict,然后,把该dict转换为关键字参数传进去

    >>> extra = {'city': 'Beijing', 'job': 'Engineer'}
    >>> person('Jack', 21, city=extra['city'], job=extra['job'])
    name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
    
    >>> extra = {'city': 'Beijing', 'job': 'Engineer'}
    >>> person('Jack', 24, **extra)
    name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    对于关键字参数,函数的调用者可以传入任意不受限制的关键字参数
    如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city和job作为关键字参数
    这种方式定义的函数如下

    def person(name, age, *, city, job):
    	print(name, age, city, job)
    
    • 1
    • 2

    和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数


    Python的函数具有非常灵活的参数形态,既可以实现简单的调用,又可以传入非常复杂的参数


    在函数内部,可以调用其他函数
    如果一个函数在内部调用自身本身,这个函数就是递归函数

    def fact(n):
    	if n==1:
    		return 1
    	return n * fact(n-1)
    
    • 1
    • 2
    • 3
    • 4

    递归函数的优点是定义简单,逻辑清晰
    理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰

    使用递归函数需要注意防止栈溢出
    在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出

    解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的

    尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式
    这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况

  • 相关阅读:
    说白了,在工作上,项目经理真离不开这2个字
    树状数组&线段树 的奇妙用法
    确保人工智能的公平性:生成无偏差综合数据的策略
    Vue中如何进行数据可视化雷达图展示
    【Linux驱动层】iTOP-RK3568学习之路(二):vscode中设置头文件路径-完成代码自动补全
    REDIS篇(4)——命令执行过程(readQueryFromClient)
    机器学习中的交叉熵
    IO流核心模块与基本原理
    tcp/ip 协议解析和开发项目传输数据的常见服务
    java-net-php-python-ssm电影影评网站计算机毕业设计程序
  • 原文地址:https://blog.csdn.net/m0_62629457/article/details/133769030