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')
# 记住起始对象和目标对象就可以了.