函数参数的类型:位置参数,默认参数,可变参数,关键字参数,命名关键字参数
1.位置参数:按照所在位置一一对应读取实参,比如指数运算函数
def power(x,n):
s=1
while n>0:
n-=1
s*=x
return s
位置参数要和实参一致。
2.默认参数:比如经常计算x^2,所以n的默认值可以为2,比如
def power(x,2):
s=1
while n>0:
s*=x
return s
这样,n就有了一个默认值2,调用时可以这样power(3)得到3的平方,但是想得到3的3次方就要power(3,3)了
def power(x,n=2):
s=1
while n>0:
s*=x
print(s)
power(3,3)
>>>27
power(3)
>>>9
默认函数有一个陷阱,比如:
def add_end(l=[]):
l.append("end")
print(l)
add_end([1,2,3]
>>>[1,2,3,'end'] #结果没问题
add_end()
>>>['end'] #结果也没问题
add_end()
>>>['end','end']
add_end()
>>>['end'.'end','end']
3.可变参数,顾名思义,就是传入的参数个数是可变的,以一道数学题为例,给定一串数字,计算它们各自的平方的和。
要定义这个函数必须确定输入的参数,因为参数的数量不确定所以可以把输入的数作为一个列表或者元组传进来,如:
def sum2(numbers):
sum=0
for i in numbers:
sum+=i*i
print(sum)
#但是调用的时候 需要先组装出列表或元组否则会报错
>>>sum2([1,2,3,4])
10
>>>sum2(1,2,3,4) #报错
如果利用可变函数就可以避免这个问题:
def sum1(*numbers):
sum=0
for i in numbers:
sum+=i*i
print(sum)
sum1(1,2,3,4)
>>>10
但在解包赋值时加了*的行为却不一样,可以看到,numbers前面加了' * ',定义可变参数和定义一个list或tuple相比只是加了一个*,在函数内部,参数numbers接收的是一个tuple,也就是收集的意思,加了该符号的形参会把收集到的实参转化为元组传递给函数,因此可以传入任意个参数也可以是0个:
>>>sum1()
0
序列解包能交换变量的值,将一个可迭代对象解包,并将得到的值存储到一系列变量中
value=1,2,3
>>>value
(1,2,3)
>>>x,y,z=value
>>>x
1
>>>y
2
>>>z
3
a,s,d='the'
>>>print(a)
t
在使用返回元组的函数或方法很有用
这样能够返回被打包成元组的多个值,然后通过一条赋值语句轻松的访问这些值。
secondrel={’bob':'name','girlfriend':'marion'}
key,value=secondrel.popitem()
>>>print(key)
girlfriend
>>>print(value)
marion
如果已经有了一个list或tuple,要调用一个可变参数可以这样
nums=[1,2,3]
sum1(nums[0],nums[1],nums[2])
然而这样太繁琐,所以py允许在list和tuple前加一个*号,把list或tuple的元素变成可变参数传进去:
>>>nums=[1,2,3]
>>>sum1(*nums)
14
可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时会自动组装为一个tuple,而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict:
4.关键字参数:
def person(name,age,**kw):
print('name:',name,'age:',age,'other:',kw)
>>>person('bob',30)
name:bob age:30 other:{}
也可以传入任意个数的关键字参数
>>>person('adam',45,gender='M',job='enginner')
name:adam age:45 other:{'gender'='M','job'='enginner'}
关键字函数可以拓展函数的功能。比如,在person函数里,保证能接收到name和age两个参数,但是如果调用者愿意提供更多的参数,person也能收到,和可变参数类似,也可以先组装出一个dict,然后把该dict转换为关键字参数传进
**extra表示把extra这个dict的所有key-value用关键字参数传入到函数的**kw参数,kw将获得一个dict,注意kw获得的dict是extra的一份拷贝,对kw的改动不会影响到函数外的extra
5.命名关键字参数
对于关键字参数,函数的调用者可以传入任意b不受限制的关键字参数。至于到底传入了哪些,就需要在函数内部通过kw检查。这就需要用到命名关键字参数
def perosn(name,age,*,city,job):
print(name,age,city,job)
>>>person('bob',24,city='beijing',job='teacher')
bob 24 beijing teacher
命名关键字参数必须传入参数名,这和位置参数不同,如果没有传入参数名,调用将报错,命名关键字参数有缺省值,从而简化调用
最后函数定义的顺序必须是:必选参数,默认参数,可变参数,命名关键字参数和关键字参数
解包的序列包含的元素个数必须与你在等号左边列出的目标个数相同,否则会引发value error的异常
可以使用*运算符收集多余的值
>>>a,b,*rest=[1,2,3,4]
>>>print(rest)
[3,4]
可以放在中间的位置
a,*rest,b=[1,2,3,4]
print(rest)
[2,3]
带*号的变量总是最终包含的总是一个列表,和函数中的手机成元组不一样
链式赋值&增强赋值
增强赋值就是形如x+=1的语句,链式赋值指一种快捷方式,如:x=y=some function()
接下来是变量,可将其视为指向值的名称。实际上有一个名为vars的函数用来返回不可见的字典
x=1
y=2
scope=vars()
print(scope['x']) #返回值为1
这种看不见的字典称为全局作用域,而对于每一个函数,都有属于自己的独立的命名空间
def foo():x=42;return x
x=1
>>>print(x)
1
>>>print(foo())
42
>>>print(x)
1
这里函数foo修改了变量x但是最终的x没有变因为调用foo时创建的一个新的作用域,供foo的代码使用,赋值语句x=42是在内部作用域完成的,不影响外部作用域的x。参数类似局部变量,因此参数与全局变量同名不会有任何问题。
def combine(parameter):print(parameter+external)
external='berry'
combine('shrub') >>>shrubberry
这样访问全局变量是众多bug的根源,务必慎用全局变量.如果有一个局部变量或参数与你要访问的全局变量同名,就无法直接访问全局变量,因为被局部变量覆盖。可以使用global重新关联全局变量。
x=1
def change_global():
global x #global后的变量声明为全局变量
x+=1
print(x)
change_global()
2
作用域嵌套
py函数可以嵌套,即将一个函数放在另一个函数里
def foo():
def bar():
print('hello,world')
bar()
def multiplier(factor):
def multiplyfactor(number):
return number*factor
return mutiplyfactor(int(input())
>>>multiplier(4)
5
20
在这里,一个函数位于另一个函数中,且外面的函数返回里面的函数,也就是返回一个函数,而不是调用它
重要的是,返回的函数能够访问其定义所在的作用域,它携带着自己所在的环境(和相关的局部变量)每当外部函数被调用时经将重新定义内部的函数而变量factor的值也可能不同。由于Python的嵌套作用域,内部函数访问的是外部局部作用域(函数multiplier的作用域)的变量,而非全局变量,这是Python中一个强大的结构,称之为——闭包.在闭包中的函数不能够给闭包函数中的变量赋值,你能够给外部作用域(非全局作用域)内的变量赋值.闭包的优点和缺点是一致的,
递归函数分为基线条件和递归条件,基线条件:满足这种条件时返回一个值,递归条件:包含一个或多个调用,旨在解决问题的一部分,函数调用自身时,是两个不同的函数,就像是f(f(x))
一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中,不会在f1调用后被自动清除。
原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制回收。举例说明:计算阶乘:
def factorial(n):
if n==1:
return 1
else:
return n*factorial(n-1)
print(factorial(3))
>>>6
利用循环实现:
def factoral(n):
result=n
for i in range(1,n+1):
result*=i
return result
print(factoral(3))
>>>6
lambda表达式(匿名函数)
p=lambda x,y:x+y
>>>print(p)
<function <lambda> at 0x000001DC832D8BF8> #可以看出这是一个函数
>>>print(2,3)
6
练习题:
以下函数允许计算两个数的乘积,请稍加改造,变成可接收一个或多个数并计算乘积:
def product(x, y):
return x * y还需要加上测试代码:
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('测试成功!')'''
暂时的做法:
def product(*nums):
que=1
if nums!=():
for i in nums:
que*=i
return int(que)
else:
nums=int(nums)
return nums
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('测试成功!')
结果:product(5) = 5
product(5, 6) = 30
product(5, 6, 7) = 210
product(5, 6, 7, 9) = 1890
测试成功!