Python函数精讲

Python 函数精讲

1. 调用函数

1.1 核心知识点

  • python内置很多函数,可以参考python官方网站了解内置的函数了解每个内置函数的具体用法;
  • 每个函数对应的参数个数和参数类型都是不一样的,个数和类型传入错误是会有不同错误提醒的,下面以求绝对值函数abs来进行说明
#参数个数错误
>>> abs(-12.3)
12.3
>>> abs(-12.3,d)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: abs() takes exactly one argument (2 given)

#参数数据类型错误
>>> abs('ddd')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: bad operand type for abs(): 'str'py

1.2 常用的几个数据类型转换函数

hex():整数转换为十六进制字符串格式

int():将字符串转换为整数类型

str():将其他类型转换为字符串

bool():返回True或者False,只要参数不是0,不是空列表,空元组等,都会返回True,其他情况返回False

float():将其他类型转换为浮点数

演示代码:

>>> int('255')
255
>>> int(2.55)
2

>>> float(12)
12.0
>>> float('12')
12.0
>>> float('1234.34')
1234.34

>>> str(['w',3,'e'])
"['w', 3, 'e']"
>>> str(123)
'123'

>>> bool(0)
False
>>> bool(1)
True
>>> bool([2,3])
True
>>> bool([])
False
>>> bool(())
False
>>> bool((1))
True
>>> bool((1,))
True
>>> bool({
    
    })
False
>>> bool({
    
    'name':'xiao'})
True

>>> hex(123)
'0x7b'

1.3 函数别名

​ 函数名就是指向函数对象的一个引用,所以可以给函数名其别名。

>>> i = int
>>> i(12.5)
12

2. 定义函数

​ 定义函数使用def,然后后面跟上函数名,小括号,小括号里是参数,冒号,然后在缩进块中编写函数体,最后函数返回使用return语句返回。

演示代码:

写一个求绝对值的函数:

def my_abs(x):
	if x>= 0:
		return x
	else:
		return -x
		
print(my_abs(9))

print(my_abs(0))

print(my_abs(-3))

函数的返回值

​ 函数内部只要遇到return函数就执行结束,并且返回值,因此可以通过条件判断和循环语句来实现复杂的逻辑关系;

​ 如果函数体没有return语句,函数也会返回值,返回值为None,return None可以简写为return.

导入函数:

​ 进入python交互模式,使用form 文件名 import 函数名导入

演示代码:

>>> from test2 import my_abs
>>> my_abs(100)
100

空函数:

​ 函数体使用pass语句来定义一个空函数,pass起到一个占位的作用,保证函数没有语法错误,程序可以运行起来

演示代码:

def kong_hanshu(s):
	pass

参数检查:

​ 一个完整的函数定义是应该有参数检查的,在参数出现错误的时候报相应的错误TypeError,而不是造成函数体执行错误。

​ 下面还以自定义求绝对值函数来进行演示:

def my_abs(x):
	if x>= 0:
		return x
	else:
		return -x
     //缺少参数定义的函数my_abs
>>> from test2 import my_abs
>>> my_abs('A')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\cdata\Desktop\test2.py", line 2, in my_abs
    if x>= 0:                               //自定义函数缺少参数类型检查时会造成函数内部if条件判断执行错误
TypeError: '>=' not supported between instances of 'str' and 'int'
    
>>> abs('A')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: bad operand type for abs(): 'str'  
        //python内置的abs函数有参数检查步骤,因此在参数检查步骤就会报错,而不会走到函数体内部,对人造成误解

 //下面在my_abs上增加函数检查步骤,这样函数就会在第一步进行参数检查,如果类型不对就报错:
>>> def My_abs(x):
...     if not isinstance(x,(int,float)):
...         raise TypeError('bad operand type')
...     if x >= 0:
...         return x
...     else:
...         return -x
...
>>>
>>> My_abs('A')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in My_abs
TypeError: bad operand type
    

链接:内置函数isinstance说明

返回多个值:

​ 当返回多个值的时候,其实返回的是一个tuple,返回值时单一的,在语法上,当返回一个tuple时可以省略括号,并且多个变量可以按照位置接收tuple里的元素,所以可以将返回多个值的函数一次赋值给不同变量。

​ 下面以移动坐标进行代码演示:

import math              //导入包math,这样后面的代码就可以使用这里面的cos,sin,pi等函数

def move(x,y,distance,angle=0):
    nx = x+distance*math.cos(angle)
    ny = y-distance*math.sin(angle)
    return nx,ny

>>> n,m = move(20,20,30,math.pi/6)
>>> print(n,m)
45.98076211353316 5.000000000000002    //返回两个值
>>> r = move(20,20,30,math.pi/6)
>>> print(r)
(45.98076211353316, 5.000000000000002) //其实是返回一个tuple

3. 函数的参数

​ 定义函数的时候,我们把参数的名字和位置确定下来,函数的接口就定义好了,然后调用者只需知道需要传入的参数和返回结果就可以了,而不用明白函数内部复杂的逻辑关系。

​ python函数定义非常简单,但是灵活性非常大,不但可以定义必选参数,还可以定义默认参数,可选参数,关键字参数,使得函数定义出来的接口不但能够接收复杂的参数,而且还可以简化调用者的代码。

参数分类及作用:

位置参数::调用函数时按照位置,将传入的值按照位置依次赋给参数,比如下面case中的x,n就是两个位置参数

Case::求x的n次方的函数

def power(x,n):
    s =1
    while n>0:
        s=s*x
        n=n-1
    return s
    	

必选参数:不给位置参数赋默认值,就是必选参数

默认参数:给位置参数赋个默认值,在传入参数的时候可以不传入这个参数,不但简化函数的调用,而且少传入这个参数时也不会报错,并且在想实现复杂的调用时,又可以传递更多的参数来实现。

​ 默认参数必须在必选参数后面,我们一般将变化大的参数作为必选参数,变化小的作为默认参数,比如一个学生信息表中,姓名和性别就可以作为必选参数,而城市和年龄这两项变换小的就可以作为默认参数。

Case1::求x的n次方的函数,默认n=2

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

Case2::按照输入输出学生信息表

def class_info(name,gender,age=3,city='shenzhen'):
    print('name is:',name)
    print('gender is:',gender)
    print('age is:',age)
    print('city is:',city)
    
>>> class_info('xiao','M')    //只传入必选参数
name is: xiao
gender is: M
age is: 3
city is: shenzhen
>>> class_info('xiao','M',6)  //按顺序传入参数
name is: xiao
gender is: M
age is: 6
city is: shenzhen
>>> class_info('xiao','M',6,'beijing')   //按顺序传入参数
name is: xiao
gender is: M
age is: 6
city is: beijing
>>> class_info('xiao','M',city='beijing')  //不按顺序传入参数,需要把参数名写上
name is: xiao
gender is: M
age is: 3
city is: beijing

Case3::默认参数是可变对象时的坑

>>> def add_END(L=[]):
...     L.append('END')
...     return L
...
>>>
>>> add_END([1,2,3])
[1, 2, 3, 'END']
>>> add_END([2,3,4])
[2, 3, 4, 'END']               //如果给默认参数传入值,那么是没有问题的

>>> add_END()
['END']
>>> add_END()
['END', 'END']          //如果使用默认参数,那么上一次调用计算出来的值将会存储在可变变量中,下次调用时会调用
						//因此定义默认参数时一定不能是不可变对象
    
 //所以上面的函数可以改成None这个不变对象
def add_end(L=None):
    if L == None:
        L =[]
    L.append('END')
    return L

为什么设置str,None这些不变变量呢?

不变对象一旦建立,元素就确定了,不用担心因为修改数据而造成的错误,并且多任务环境下读取对象不用加锁,同时读取没有问题,因此我们在设计程序时优先设置不变对象。

可变参数:使用*参数的格式定义一个可变参数,应用在参数个数不确定的情况下,与list参数或者tuple参数是一个效果,只不过在函数调用时采用可变参数的格式会更加简洁。

​ 1.可变参数可以传入0个或者任意多个参数,在函数调用时自动封装成一个tuple。

​ 2.可变参数可以直接传入fun(1,2,3),也可以先封装成list或者tuple,然后在前面加上*号来传入fun(*[1,2,3])

Case:可变参数和list/tuple参数的对比,求a2 + b2 + c2 + ……的和

//list/tuple参数方式
def calc(L):
    sum=0
    for n in L:
        sum = sum + n*n
    return sum

>>> calc([2,3,4])
29
>>> calc((2,3,4))
29
>>> calc(2,3,4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: calc() takes 1 positional argument but 3 were given   //调用的时候必须封装成list或者tuple,否则会报错

>>> L = [2,3,4]
>>> calc(L)
29                                //对于已经存在的llist或者tupler,可以直接调用

//可变参数方式
def calc(*L):
    sum=0
    for n in L:
        sum = sum + n*n
    return sum

>>> calc(L[0],L[1],L[2])
29
>>> calc(*L)
29                       //调用已经存在的list或者tuple时,使用*list或者*tuple的方式调用很简便

>>> calc(2,3,4)
29                      //传入list或者tuple时也可以做简化

关键字参数:可以接收0个或者任意多个参数,在函数调用时封装成dict

​ 关键字参数的作用时函数的扩展,像用户注册这种功能的实现,前面的姓名,性别是必选参数,其他的年龄城市可以使用关键字参数做成可选功能。

​ 1.关键字参数可以直接传入fun(a=1,b=2),也可以再已经封装好的dict前面加**,如fun(**{‘a’:1,‘b’:2})

Case:用户注册信息函数的定义和调用

def user_info(name,genter,**kw):
    print('name:',name,'genter:',genter,'other:',kw)
    
>>> user_info('xiao','M',age=18,city='shenzhen')
name: xiao genter: M other: {
    
    'age': 18, 'city': 'shenzhen'} //直接传入,关键字参数被封装成dict
            
>>> dict1={
    
    'age':18,'city':'shenzhen'}
>>> user_info('xiao','M',age=dict1['age'])
name: xiao genter: M other: {
    
    'age': 18}                  //对于以存在的dict的调用
>>>
>>> user_info('xiao','M',**dict1)
name: xiao genter: M other: {
    
    'age': 18, 'city': 'shenzhen'}            //对于存在的dict的调用,简便方法
            //注意:**dict1是将dict1内部的key-value作为关键字参数传入到函数的**kw参数,而外部的dict1是没有变化的。

命名关键字参数:

​ 1.关键字参数无法对传入的参数进行限制,命名关键字参数可以对关键字参数进行限制,定义的关键字参数在调用的时候必须传入。

​ 2.当命名关键字参数与位置参数前面使用*隔开,否则命名关键字参数将会被视为位置参数

​ 3.如果命名关键字参数前面有可变参数,那么*号分隔符可以省略。

​ 4.命名关键字参数可以定义默认值。

Case:

def user_info(name,genter,*,city,age):
    print('name:',name,'genter:',genter,'city:',city,'age:',age)
    
>>> user_info('xiao','M',age=19,city='shenzhen')
name: xiao genter: M city: shenzhen age: 19                 //命名关键字参数
                
def user_info(name,genter,*args,city,age):
    print('name:',name,'genter:',genter,'agrs:',args,'city:',city,'age:',age)    
>>> user_info('xiao','M',(1,2,3),city='beijing',age=18)
name: xiao genter: M agrs: ((1, 2, 3),) city: beijing age: 18   //带可变变量的命名关键字参数
                    
def user_info(name,genter,*,city='shenzhen',age):
    print('name:',name,'genter:',genter,'city:',city,'age:',age)
>>> user_info('xiao','M',age=18)
name: xiao genter: M city: shenzhen age: 18
>>> user_info('xiao','M',age=18,city='guangzhou')
name: xiao genter: M city: guangzhou age: 18              //命名关键字参数赋予默认值  

参数组合:上面介绍的五种参数可以组合使用。

​ 1.组合中顺序为,必选参数,默认参数,可变参数,命名关键字参数,关键字参数

​ 2.调用函数时会根据参数位置和参数名将将参数传进去

​ 3.无论函数如何定义,都可以使用func(*args,**kw)的格式来调用,可变参数和关键字参数时pyhton的习惯写法,最好这样使用

​ 4.虽然参数可以组合,但是尽量减少组合的参数类型,否则会增加代码的可读性

Case:

def fun1(a,b,c=5,*arg,**kw):
    print('a:',a,'b:',b,'c:',c,'arg:',arg,'kw:',kw)
    
>>> fun1(1,2,3,*[6,7,8,9,10],d=7,e=10)
a: 1 b: 2 c: 3 arg: (6, 7, 8, 9, 10) kw: {
    
    'd': 7, 'e': 10}  //传入必选参数的形式
 
>>> fun1(*[6,7,8,9,10],d=7,e=10)
a: 6 b: 7 c: 8 arg: (9, 10) kw: {
    
    'd': 7, 'e': 10}
                      //不传入必选参数,传入可变参数,将可变参数前面的值根据位置依次传给必选参数和默认参数

练习:计算一个或多个数的积

def product(*x):
    if x==():               
        raise TypeError              //这里需要加入一个错误处理,当传入的是空tuple时,需要报错,提前终止程序
    s = 1                           //tuple不是None,所以这里if判断不能写成x==None,但是可以写成x is ()
    for n in x:
        s = s*n
    return s
    
# 测试
print('product(5) =', product(5))
print('product(5, 6) =', product(5, 6))
print('product(5, 6, 7) =', product(5, 6, 7))
print('product(5, 6, 7, 9) =', product(5, 6, 7, 9))
if product(5) != 5:
    print('测试失败!')
elif product(5, 6) != 30:
    print('测试失败!')
elif product(5, 6, 7) != 210:
    print('测试失败!')
elif product(5, 6, 7, 9) != 1890:
    print('测试失败!')
else:
    try:
        product()
        print('测试失败!')
    except TypeError:
        print('测试成功!')

4. 递归函数

​ 递归函数就是在函数体内部再次调用函数本身,比如n!就是一个典型的递归函数;

​ 递归函数的缺点是容易栈溢出。

​ 递归函数的优点是逻辑清晰,容易理解。

​ 所有的递归函数都可以写成循环的方式,但是循环的逻辑不如递归清晰

​ 没有循环函数的编程语言采用尾递归来实现循环。

练习:

def f(n):
    if n == 1:
        return 1
    return n*f(n-1)

>>> f(1)
1
>>> f(3)
6
>>> f(5)
120
>>> f(100)
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

栈溢出解释:

​ 在计算机中,函数的调用是使用栈这种数据结构,函数调用会在栈上添加一层帧,函数返回会减去一层帧,所以如果函数调用次数太多就会造成栈溢出。

尾递归:

​ 尾递归如果做了优化,在函数调用时就不会造成栈溢出,但是包括python在内的大部分编程语言都没有对尾递归做优化,所以即使做了尾递归还是会造成栈溢出。

猜你喜欢

转载自blog.csdn.net/a18829292719/article/details/108627749