05 python中的函数 - lovemoganna/DataStructure GitHub Wiki

常用的函数

# abs函数-就是取绝对值
abs(100)
abs(-20)
abs(12.34)
abs(1,2) # 出错的原因是,传入的参数数量不对.或者传入字符串也不行的.
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-69-56688773dcb4> in <module>()
      3 abs(-20)
      4 abs(12.34)
----> 5 abs(1,2) # 出错的原因是,传入的参数数量不对.或者传入字符串也不行的.


TypeError: abs() takes exactly one argument (2 given)
# max()函数-选出最大的数
max(1,2,3,-6)
3

数据类型转换

int('123') # 将其他数据类型转为整数
int(12.34) # 12 ,取整
str(1.23) # 将其他类型转换为字符串
bool(1) # 将其他类型转换为布尔类型
bool('')# 传入为空字符串,会出False


# 函数的调用
a = abs # 函数的声明
a(-1) # 函数的调用

n1 = 255 
print(hex(n1)) # 十进制转为16进制:   0xff
0xff

定义函数

在python里面,定义一个函数要使用def语句,依次写出函数名括号括号中的参数和冒号:
    
# _*_ coding: utf-8 _*_  
# 声明一个函数
def my_abs(x):
    if x >=0:
        return x
    else:
        return -x 
# 调用函数
print(my_abs(10)) 
# 函数体内部的语句在执行时,一旦执行到return时,函数就执行完毕,并将结果返回。
    
10
# 如果将上面的函数保存为abstract.py ,那么调用这个文件里面的函数.如下:

from abstract import my_abs
my_abs(-9)
# 这样就可以调用了

空函数

# 定义一个空函数
def nop(age):
    #pass # pass可以用来作为占位符
    if age >=10:
        pass
print(nop(32)) # None
None

参数检查

# my_abs(1) # 1
# my_abs(1,2) # TypeError 

# my_abs('A') # TypeError: '>=' not supported between instances of 'str' and 'int'
# abs('A') #  bad operand type for abs(): 'str'


# 现在对不支持的参数来做出修改

def imy_abs(x):
    if not isinstance(x,(int,float)):
        raise TypeError('bad operand type')
    if x >= 0:
        return x
    else:
        return -x
imy_abs('A')    #TypeError: bad operand type
# 此时就可以出现我们自定义的错误了.
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-45-9d9d65a5e2ac> in <module>()
     15     else:
     16         return -x
---> 17 imy_abs('A')


<ipython-input-45-9d9d65a5e2ac> in imy_abs(x)
     10 def imy_abs(x):
     11     if not isinstance(x,(int,float)):
---> 12         raise TypeError('bad operand type')
     13     if x >= 0:
     14         return x


TypeError: bad operand type

返回多个值

import math
def move(x,y,step,angle=0):
    nx = x + step*math.cos(angle) # 调用sin函数
    ny = y -step*math.sin(angle)  # 调用cos函数
    return nx,ny

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

# 本质上就是一个单一值

r = move(59,37,56,math.pi/6)
print(r)# 可以看出返回值是一个Tuple.
151.96152422706632 70.0
(107.49742261192857, 9.000000000000004)
import math
print(math.cos(0.5))# 0.88
print(math.sin(0.5))# 0.48
print(math.pi / 6)# 0.52 ,也就是3.14/6
0.8775825618903728
0.479425538604203
0.5235987755982988
0.5233333333333333

总结

定义函数时,需要确定函数名和参数个数
如果有必要的话,可以先对参数的数据类型做检查.
函数体内部可以用return随时返回函数结果
函数执行完毕也没有return语句,会自定return None.
函数可以同时返回多个值,但其实就是一个Tuple.
# 模拟 ax^2+bx+c = 0 判断有解的方式:b^2-4ac>=0; 
import math 

def quadratic(a,b,c):
    if b**2-4*a*c<0:
        return "wujie"
    else:
        x1=(-b+math.sqrt(b**2-4*a*c))/(2*a)
        x2=(-b-math.sqrt(b**2-4*a*c))/(2*a)
        return x1,x2
    while True:
        a=input("a:")
        b=input("b:")
        c=input("c:")
        print(a+"x2+"+b+"x+"+c+"=0\n",quadratic(int(a),int(b),int(c)))  
quadratic(4,10,6)        
        
(-1.0, -1.5)

函数的参数

# 位置参数的使用
# 计算x^x
def power(x):
    return x*x
power(2)

# 计算x^n

def power(x,n=2):
    s=1
    while n>0:
        n = n -1
        s=s*x
    return s
power(2,3) #  查一下x^n 的多项式就知道了
8

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

默认参数

# 如果此时调用power(2),就会出错,
# 但是如果我们在 def power(x,n=2):的时候设置一下n的默认参数就不会出错了.
power(2)
power(2,3)
8

设置默认参数的时候: 1.必选参数在前,默认参数在后 2.函数有多个参数的时候,把变化大的参数放在前面,变化小的参数放在后面. 变化小的参数可以作为默认参数.

使用默认参数的好处就是降低调用函数的难度.

def enroll(name,gender='F',city='beijing'):
    print('name:',name)
    print('gender:',gender)
    print('city:',city)
enroll('xiaoming') # 这个符合条件写姓名就可以了.
enroll('donggua','M','tianjin')  # 这个不符合条件就写多点.
enroll('xiohong','M')
name: xiaoming
gender: F
city: beijing
name: donggua
gender: M
city: tianjin
name: xiohong
gender: M
city: beijing
def add_end(L=[]):
    L.append('END')
    return L 
add_end([1,2,3]) # [1, 2, 3, 'END']
add_end # <function __main__.add_end(L=[])>
add_end(['x','y','z']) # ['x', 'y', 'z', 'END']
add_end() # ['END']
add_end() # ['END', 'END']

# 定义默认参数要牢记一点:默认参数必须指向不变对象!
# 一开始的时候,默认参数L的值是[],默认参数L是一个变量,它指向[]
# 第一次调用函数,L发生了改变,即['END'],第二次调用函数是建立在第一次调用函数的基础之上的,所以会变成['END', 'END']
['END', 'END']
# 为了解决上面的问题,我们需要默认参数指向不变对象.加一个判断即可.

def add_end(L=None):
    if L is None:
        L=[]
    L.append('END')
    return L
add_end([1,2,3])
add_end([1,2,3]) # 这样就不会发生上面的那种情况了.

# 设计str,None这样的不可变对象,是为了减少由于修改数据导致的错误.
[1, 2, 3, 'END']

可变参数

# 计算 a^2+b^c^2+....
# 你声明的是一个可变参数,所以必须是list或者Tuple
def calc(numbers):
    sum =0 
    for n in numbers:
        sum = sum + n * n
    return sum

#calc([1,2]) # list
calc((1,2,3)) # tuple

14
# *number代表你输入的默认就是Tuple了.
def calc(*numbers):
    sum =0 
    for n in numbers:
        sum = sum + n * n
    return sum

calc(1,2) # 这样就默认为Tuple来进行计算了
calc() # 0 

nums = [1,2,3] # 将list作为Tuple来计算
calc(nums[0],nums[1],nums[2])

nums=[1,2,3]
calc(*nums)# 这种简化的写法,加上一个"*",就相当于把list转为Tuple了.

# *nums表示把nums这个list的所有元素作为可变参数传进去。
14

可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。

关键字参数

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

# **kw  加上了"**"就代表kw是一个关键字参数了,也就是一个dict集合
def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)
person('ming',23)    
name: ming age: 23 other: {}
person('Bob',35,city='beijing')
name: Bob age: 35 other: {'city': 'beijing'}

这个关键字参数的作用就是:可以扩展函数的功能. 场景:做一个用户注册的功能,除了用户名和年龄是必填项外,其他都是可选项,利用关键字参数来定义这个函数就能满足注册的需求。

extra={'city':'beijing','job':'Engineer'}
person('Jack',24,city=extra['city'],job=extra['job']) # 组装dict
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'}

extra表示把extra这个dict的所有key-value用关键字参数传入到函数的kw参数,kw将获得一个dict,注意kw获得的dict是extra的一份拷贝,对kw的改动不会影响到函数外的extra。

命名关键字参数

def person(name,age,**kw):
    if 'city' in kw:
        pass
    if 'job' in kw:
        pass
    print('name:',name,'age',age,'other:',kw)
person('Jack',24,city='beijing',job='manong',id='123') # 传入不受限制的关键字参数
name: Jack age 24 other: {'city': 'beijing', 'job': 'manong', 'id': '123'}
# 命名关键字参数需要一个特殊分割符*,* 后面的参数被视为命名关键字参数.
# 命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错.
def person(name,age,*,city,job):
    print(name,age,city,job)
person('xiaoming',12,city='beijing',job='kanshu')
xiaoming 12 beijing kanshu
# 命名关键字可以有缺省值,简化使用
def person(name,age,*,city='beijing',job):
    print(name,age,city,job)
person('xiaoming',12,job='kansu')# 像这种,就可以直接省去city的填写.
xiaoming 12 beijing kansu
# 如果缺少*,Python解释器将无法识别位置参数和命名关键字参数:
def person(name, age, city, job):
    # 缺少 *,city和job被视为位置参数
    pass

person('xiaoming',12,'1','2')# 此刻是正确的

def person(name, age,*,city, job):
    pass
#person('xiaoming',12,'1','2') # 此刻会出错
person('xiaoming',12,city='1',job='2') # 书写正确

参数组合

五种参数:必选参数,默认参数,可变参数,关键字参数,命名关键字参数.

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

#
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)
f1(1,2,3)
f1(1,2,3,(1,2),x=99)
f1(1,2,3,'a','b',kw={'y':7})


f2(1,2,3,d=[1],city='hello')
f2(1,2,3,d=99,ext=None)
a= 1 b= 2 c= 0 args= () kw= {}
a= 1 b= 2 c= 3 args= () kw= {}
a= 1 b= 2 c= 3 args= ((1, 2),) kw= {'x': 99}
a= 1 b= 2 c= 3 args= ('a', 'b') kw= {'kw': {'y': 7}}
a= 1 b= 2 c= 3 d= [1] kw= {'city': 'hello'}
a= 1 b= 2 c= 3 d= 99 kw= {'ext': None}
args = (5, 6, 7, 8)
kw = {'pi': 99, 'x': '#'}
f1(*args, **kw)

args=(1,3,4)
kw={'d':88,'x':'##'}
f2(*args,**kw)

# 对于任意函数,都可以通过类似func(*args, **kw)的形式调用它,无论它的参数是如何定义的。
a= 5 b= 6 c= 7 args= (8,) kw= {'pi': 99, 'x': '#'}
a= 1 b= 3 c= 4 d= 88 kw= {'x': '##'}

总结

# 默认参数一定要用不可变对象,如果是可变对象,程序运行时会有逻辑错误!
# *args是可变参数,args接收的是一个tuple;**kw是关键字参数,kw接收的是一个dict。
# 可变参数既可以直接传入:func(1, 2, 3),又可以先组装list或tuple,再通过*args传入:func(*(1, 2, 3));
def fun(a,*b):
    print('a:',a,'b:',b)
args=(1,2)   
fun(*args)

def fun2(a,**b):
    print('a:',a,'b:',b)
kw={'name':'ui','gender':'M'}    
fun2(1,**kw)


def fun3(a,*,c,**b):
    print('a:',a,'c:',c,'b:',b)
kw2={'name':'li','id':'22'}    
fun3(1,c=2,**kw2)
fun3(1,c=2,ext=None)
a: 1 b: (2,)
a: 1 b: {'name': 'ui', 'gender': 'M'}
a: 1 c: 2 b: {'name': 'li', 'id': '22'}
a: 1 c: 2 b: {'ext': None}

递归函数

阶乘

# n! = 1 x 2 x 3 x ... x n ,用函数fact(n)表示

# fact(n) = n! = 1 x 2 x 3 x ... x (n-1) x n = (n-1)! x n = fact(n-1) x n

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

fact(5)
5 * fact(4)
5*(4*fact(3))
5*(4*(3*fact(2)))
5*(4*(3*(2**fact(1))))
120* fact(1)

# fact(1000) # 栈溢出
# 在计算机中,函数的调用是通过栈(stack)这种数据结构来实现的.
# 每进入一个函数调用,栈就会增加一层栈帧,,每当函数返回,栈就会减少一层栈帧.
# 由于栈的大小不是无限的,递归调用的次数过多,会导致栈溢出.
120
def fact(n):
    if n ==1:
        return 1
    return n * fact(n-1)
fact(10)

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

尾递归

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

# 尾递归主要是把每一步的乘积传入到递归函数中.
def fact(n):
    return fact_iter(n,1)

def fact_iter(num,product):
    # 定义一个出口
    if num == 1:
        return product
    return fact_iter(num-1,num*product)

# 主要目的就是一遍一遍的获得每一步的乘积.不断的调用函数本身.
fact_iter(5,1)
fact_iter(4,5)
fact_iter(3,20)
fact_iter(2,60)
fact_iter(1,120)

# 针对尾递归优化的语言可以通过尾递归防止栈溢出。
120

汉诺塔问题描述: 有三根杆子A,B,C。A杆上有N个(N>1)穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至C杆: 1.每次只能移动一个圆盘. 2.大盘不能叠在小盘上面.

简单的想法: 1、把A 上的 n-1移动到B上 ,必须保留原来顺序 2、把A最后一个移动到C上 3、把B 上 n-1 在移动到C上。 打印出把所有盘子从A借助B移动到C的方法.

# 汉诺塔移动
B=[] #设置操作过程列表
def move(n, a, b, c):
    if n==1:
        buzhou=a+str(n)+'-->'+c+str(n) #一个圆盘需要从A到C操作步骤
        B.append(buzhou) #向列表中添加操作步骤
        return
    else:
        move(n-1,a,c,b) #将A柱的n-1个盘移到B柱
        move(1,a,b,c) #将A柱上最后一个盘移到C柱
        move(n-1,b,a,c) #将过渡柱子B上n-1个圆盘B移动到目标柱子C
        print('共需操作' + str(len(B)) + '次', '操作过程为', B)
move(3,'A','B','C')        

# 记住起始对象和目标对象就可以了.
⚠️ **GitHub.com Fallback** ⚠️