Python基础语法快速复习-函数

1、定义函数

参数检查

观察自己设置的绝对值函数(my_abs)和内置函数abs的报错区别:

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

my_abs('A')
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-1-3a70387458fd> in <module>
      5         return -x
      6 
----> 7 my_abs('A')


<ipython-input-1-3a70387458fd> in my_abs(x)
      1 def my_abs(x):
----> 2     if x>=0:
      3         return x
      4     else:
      5         return -x


TypeError: '>=' not supported between instances of 'str' and 'int'
abs('A')
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-2-6eee3e7a739d> in <module>
----> 1 abs('A')


TypeError: bad operand type for abs(): 'str'

观察上方的报错信息,内置函数abs会检查参数错误而我们自己定义的函数不会,所以我们的函数定义不够完善。

对my_abs的定义进行修改,只允许整数和浮点数类型的参数,进行数据类型的检查。

isinstance() 函数来判断一个对象是否是一个已知的类型,类似 type()。

isinstance()语法规范:isinstance(object, classinfo)

classinfo:可以是直接或间接类名、基本类型或者由它们组成的元组。

#添加参数检查
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')
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-3-f13543a1b7d8> in <module>
      7     else:
      8         return -x
----> 9 my_abs('A')


<ipython-input-3-f13543a1b7d8> in my_abs(x)
      2 def my_abs(x):
      3     if not isinstance(x,(int,float)):
----> 4         raise TypeError('bad operand type')
      5     if x>=0:
      6         return x


TypeError: bad operand type

返回多个值

import math

def move(x,y,step,angle = 0):
    nx = x+step*math.cos(angle)
    ny = y-step*math.sin(angle)
    return nx,ny
#import math语句表示导入math包,也就是导入相应sin和cos之类的数学函数

x,y = move(100,100,60,math.pi/6)
print(x,y)
151.96152422706632 70.0

其实这是一种返回多个值的假象,实际上python函数返回的是一个tuple,多个变量可以同时接收一个tuple,按位置赋给对应的值。

练习

定义一个函数quadratic(a,b,c),接收三个参数,返回一元二次方程Python基础语法快速复习-函数的两个解

计算平方根调用math.sqrt()函数:

import math

def quadratic(a,b,c):
    d = b*b-4*a*c
    if d>=0:
        x1 = (-b+math.sqrt(d))/(2*a)
        x2 = (-b-math.sqrt(d))/(2*a)
        return x1,x2
    else:
        return '此方程无解'

quadratic(1,2,1)
(-1.0, -1.0)

2、函数的参数

定义函数接口的基本要求:确定参数的名字和位置即可。

位置参数

编写计算某数平方的函数

def power(x):
    return x*x
print(power(5))
print(power(10))
25
100

以上代码中,x为位置参数,调用函数必须传入对应个数的位置参数

编写计算x^n的函数

def power(x,n):
    s = 1
    while n > 0:
        n = n-1
        s = s * x
    return s
print(power(5,2))
print(power(5,3))
25
125

传入位置参数时不仅要保证个数相同,还要保证和定义函数时候的顺序也相同(直接用函数名的话可以忽略顺序)

默认参数

给幂设置默认值

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

设置默认参数的注意事项:

(1)必选参数在前,默认参数在后。

(2)当函数有多个参数时,把变化大的参数放在前面,变化小的参数放在后面(变化小的参数就可以作为默认参数)

设置默认参数的好处:降低调用函数的难度。

学生注册问题:来自相同的城市,拥有相同的年龄,只有名字和班级不一样。

def enroll(name,gender,age=6,city='Beijing'):
    print('name',name)
    print('gender',gender)
    print('age',age)
    print('city',city)
enroll('Sarah','F')
name Sarah
gender F
age 6
city Beijing

定义默认函数时候的注意点:默认参数必须指向不变对象

例如:如果默认参数指向一个列表,在运行函数时会将列表看作一个变量,改变列表的内容后下次调用函数列表的初始内容就会发生改变。

可以使用None这种不变对象来设置默认参数值。

def add_end(L = []):
    L.append('END')
    return L
add_end()
add_end()
add_end()
['END', 'END', 'END']

可变参数

定义具有可变参数的函数,必须确定输入参数。

可变参数在函数调用时自动组装成为一个tuple。

定义可变参数比定义一个list或tuple参数相比,仅仅在参数前加了一个 * 号。在函数内部,可以传入任意个参数,包括0个参数。

def calc(*numbers):
    sum = 0
    for n in numbers:
        sum = sum+n*n
    return sum
print(calc(1,2))
print(calc())
5
0

关键字参数

关键字参数允许传入0个或任意个含参数名的参数,这些关键字参数被自动组装成一个dict。

函数除了需要接受必须参数外,还可以接受关键字参数kw,传入关键字参数后会以字典的形式呈现出来

def person(name,age,**kw):
    print('name:',name,'age:',age,'other:',kw)
print(person('Michael',30))
print(person('Bob',35,city='Beijing'))
name: Michael age: 30 other: {}
None
name: Bob age: 35 other: {'city': 'Beijing'}
None

关键字参数可以扩展函数功能,接收到调用者提供的更多参数(例如必填项之外的选项)

** +字典名表示把字典内所有的key-value用关键字参数传入到函数 ** kw参数中,kw仅仅是dict的一份拷贝。

命名关键字参数

限制关键字参数的名字,需要用命名关键字参数,在参数之间加上特殊的分隔符 * 即可,分隔符后面的参数即为命名关键字参数。

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

person('Jack',24,city='Beijing',job='Engineer')
Jack 24 Beijing Engineer

如果函数定义中已经含有可变参数,那么不需要分隔符,只需要在可变函数后加上命名关键字参数即可。

def person(name,age,*args,city,job):
    print(name,age,args,city,job)
person('Jack',24,city = 'Beijing',job='Engineer')
Jack 24 () Beijing Engineer

函数参数的定义顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。

def f1(a, b, c=0, *args, **kw):
    print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)

def f2(a, b, c=0, *, d, **kw):
    print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
f1(1, 2)
a = 1 b = 2 c = 0 args = () kw = {}
f1(1, 2, c=3)
a = 1 b = 2 c = 3 args = () kw = {}
f1(1, 2, 3, 'a', 'b')
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}
f1(1, 2, 3, 'a', 'b', x=99)
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
f1(1, 2, 3, 'a', 'b', x=99)
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
f2(1, 2, d=99, ext=None)
a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}

3、递归函数

求阶乘的函数 f(n) = n! = 1×2×3×⋅⋅⋅×(n−1)×n=(n−1)!×n=f(n−1)×n

所以f(n)可以表示为n * f(n-1)

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

print(f(1))
print(f(5))
print(f(100))
1
120
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

使用递归函数可能会导致栈溢出的问题(调用次数过多的时候)

解决递归调用栈递归的方法是尾递归优化

尾递归:在函数返回的时候调用自身本身,且return语句不能包括表达式,能够让递归函数只使用一个栈帧,不出现栈溢出。上面的f(n)在return语句中使用了乘法表达式,不能算作尾递归。

def fact_iter(num,product):
    if num==1:
        return product
    return fact_iter(num-1,num*product)

fact_iter(5,1)
120

上述为尾递归调用,大多数编程语言没有针对尾递归的优化,python也没有,所以即使将f(n)修改成尾递归方式也会导致栈溢出

上一篇:js基础之函数


下一篇:mysql 获取离时间比较近的一条数据