python函数
一、python 函数
1.函数
1.1函数定义
python 函数由几个句块、函数名称和参数列表组成。它是组织代码最小的单元.
具有一定的完成功能.**
2.2函数的作用
结构化编程包装最基本的代码,并根据功能组织代码
封装是为了重用,冗余代码较少
代码更简单,更容易理解
3.3函数分类
内建函数 如max() min() reversed()
函数库如math.ceil()
自定义函数 使用def() 定义
2.函数定义
def 函数名 (参数列表)
函数体(代码块)函数体
[return 返回值]
函数名称为函数标识符,命名要求相同
句块必须缩进,python要求严格缩进句子
如果没有函数return然后默认返回none空
定义中的参数列表是一种形式参数,即形式参数
3.函数调用
函数定义是指声明函数尚未调用,不能执行,只能调用
调用方式 即在函数后面添加() ,有必要在()中添加参数
调用时写入的参数为实际参数,即实际参数
def add(x,y): #函数定义 result = x y #代码块 return result #返回值 out = add(5,6) #调用函数 print (out) #打印输出值 #调用函数时占用内存
解析:
定义函数名称add其形状参考函数x,y
计算函数 x y 并赋值结果result,提供返回值
调用时需要提供两个实参赋值y, 本案实际参与 5和 6
函数名也是标识符
返回值也是值,默认不提供return那么会返回none
定义时必须有函数,否则会爆炸NameError
函数是可调对象,因此可以被调用callable(add) 回车 true
4.函数参数
函数定义是定义形参,调用时调用实参。一般来说,形参与实参对应(可变形参除外)
4.1 实参传参
1.位置传参
定义时(x,y,z),调用时(1,2,3)
2.关键字传参
定义时(x,y,z)调用时(x=1,z=2,y=3) 如果将形参名称传入实参,位置无所谓
def add(x,y): print (x) print (y) print ('_' * 30) add(4,5) add(5,4) add(x=4,y=5) add(x=[4],y=(5,)) add(x=4.1,y=5.1) dd(x=5,4)
add(y=4,5)
#测试以上实验都是什么结果 为什么?
切记: 传参是指调用时穿的实参,就两种方式,一个是位置一个是关键字不可混用
4.2形参缺省值
给予形参一个缺省值,
def add(x=1,y=2):
print (x)
print (y)
print ('_' * 10 )
add(3)
add(2,3)
add(x=3,y=4)
#各输出什么?
# 定义一个函数login,参数名称为host、port、username、password
def mysqllogin(host='mysql_login.com',port='3306',username='admin',password='123456'):
print ('mysql://{2}:{1}@{0}:{3}'.format(host,port,username,password))
mysqllogin()
mysqllogin('127.0.0.1')
mysqllogin('127.0.0.1','3307','mysql_user','mysql_password')
#会输出什么?
当给予形参默认值时,当调用函数传入实参时只给部分实参,那么就会使用形参缺省值
4.3可变参数
*args 打包输出元组, *kwargs是打包输出字典
需求: 需要写一个函数对多个数字进行求和
def sum(k):
s = 0
for i in k :
s += i
return s
print (sum([1,2,3,4,5,6]) ) #直接替代为range(10) 可以吗?
#此方法不支持元组,因为+=不支持int和list
def sum(*nums) :
s = 0
for i in nums:
s +=i
return s
pirnt (sum(1,2,3,4,5,6))
传入可迭代参数,并且累加每一个参数
*代表可变位置参数,接受多个实参,他将收集的实参组织到一个元组中tuple\
4.3.1可变位置参数
即为*,比如以上案例使用 *nums,代表可变位置参数,接受多个实参放入tuple
4.3.2可变关键字参数
**代表可变关键字参数,接受多个关键字传参
将收集的值组织到一个字典中
def showconfig(**kwargs):
for k,v in **kwargs :
print ('{}={}'.format(k,v),end=',')
showconfig(host='127.0.0.1',port='3306',username='admin',password='123456')
def showconfig(username,password,**kwargs,*args):
for k,v in kwargs.items():
print('{}={}'.format(k,v), end=', ')
showconfig(host='127.0.0.1', port=8080, username='wayne', password='magedu')
def showconfig(username,password,*args,**kwargs):
for k,v in kwargs.items():
print('{}={}'.format(k,v), end=', ')
showconfig(host='127.0.0.1', port=8080, username='wayne', password='magedu')
def showconfig(username,password,**kwargs):
for k,v in kwargs.items():
print('{}={}'.format(k,v), end=', ')
print (**kwargs,username,password)
showconfig(host='127.0.0.1', port=8080, username='wayne', password='magedu')
#想想输出结果,为什么?
总结:
可变位置参数*表示, 可变关键字传参 **表示
可变位置传参将参数收集成tuple元组 可变关键字传参收集成dict 字典
在混合使用时普通形参要放到前边,可变位置参数要放到可变关键字传参前边
练习:
def comprehensive(*args,**kwargs):
for i in args:
print (i)
for k,v in kwargs.items():
print (k,v )
l1=[*range(10)]
comprehensive(l1,host='127.0.0.1',username='123')
#输出结果?
def fn(x,y,*args,**kwargs):
print (x,y,args,kwargs,end = '\n')
fn(1,2,3,4,5,6,7,x=1,y=2,a='123',b='abc')
fn(x=1,y=2,1,2,3,4,a='abc',b='123')
#位置参数跟随关键参数!这是不被允许的!
SyntaxError: positional argument follows keyword argument
4.4 keyword-only参数
def fn(*args, x, y, **kwargs):
print(args, x, y, kwargs, end='\n')
fn(1,2,3)
fn(1,2,3,a='123',b=2)
fn(1, 2, 3, 4, x=1, y=2, a='123', b=3)
#TypeError: fn() missing 2 required keyword-only arguments: 'x' and 'y'
当python3 更新之后定义一个keyword-only函数,在一个*可变位置参数之后的形参都会被定义为keyword-only,必须传入.
变相思考,是不是*args可变位置参数已经获取了全部参数位置带入到tuple中,不可使用位置参数只能使用关键字传参.
那么**kwargs可以吗,可变关键字传参?
def fn(**kwagrs,x,y):
print (kwargs,x,y)
fn(host='127.0.0.1',username='123',y='100')
#SyntaxError: invalid syntax
可变关键字传参概念:将输入的参数存储到dict,那么x,y是不是关键字?就算写入x=也无法得到值
语法不存在
如果想要所有形参都变成keyword-only
def fn(*,x,y) :
print (x,y)
fn(x=10)
#TypeError: fn() missing 1 required keyword-only argument: 'y'
总结在*可变位置传参后所以形参都会变成必须需要传入的关键字参数.
4.5 Positional-only参数
def fn( a , / ) :
print (1)
fn(a=10)
fn(10)
#TypeError: fn() got some positional-only arguments passed as keyword arguments: 'a'
翻译过来的意思仅能使用位置传参,所以说比较鸡肋不太使用.
4.6 参数混合使用
将 *可变位置参数 **可变关键字参数 x='abc’缺省值 *,a=1 keyword-one ,/Positional-only进行配合使用
def fn( *args,a=10, ): #可变位置传参,关键字传参
print( args, a)
def fn2(x,*args,y=100) : #位置传参,可变位置传参,关键字传参
print (x,args,y)
def fn3(x,y,*args,k=100,**kwargs) : #位置传参,可变位置传参,关键字传参,可变关键字传参
print (x,y,args,k,kwargs)
def fn4(*args,x,y,k='abc',**kwargs): #可变位置传参,keyword-noly,关键字传参,可变关键字传传参
print (args,x,y,k,kwargs)
def fn5 (x=100,y=200,k,*args,**kwargs, / ):
print(x,y,k,args,kwargs)
fn(1)
fn(1,20,a=30)
fn2(1,1,2,3,3,4,5,6,y=30)
fn3(1,2,2,3,4,5,6,l=100,n=200,k=500)
fn4(1,2,4,4,5,6,x=100,y=200,k=600,mm='sun',gg='wangwang') #爆x,y,keyword-only关键字传参
fn5(x=200,y=300,100,1,2,3,4,56,kw=10000,wk=20000) #fn / Positional-only只支持前边是关键值传参
4.7 参数使用规则
一般来说写函数使用参数一定要简单易懂
一般的使用规则为: Positional-only,普通参数,缺省参数,可变位置参数,keyword-only(带缺省值),可变关键字参数.
def fn(a,b,/,c,d,e=200,*args,k=100,w=299,**kwargs):
print (a,b,c,d,e,args,k,w,kwargs)
fn(100,200.300,400.500,1,2,3,4,5,k=200,w=300,admin='liu')
#应用
def com(host='127.0.0.1',username='admin',password='123456',port='3306',**kwargs):
print ('mysql://{}:{}@{}:{}\{}'.format(username,password,host,port,kwargs.get('db','test')))
com(db='wordpress')
com(host="192.168.1.100",username='Administrator',password='Liujinxin@123',port='33060',db='work-01')
总结: 定义最常使用参数为普通参数,不是提供缺省值,需要客户提供,注意参数顺序.
将必须使用的参数定义为keyword-only参数,必须携带的,要求关键字必须传入
如果函数有很多的参数无法逐一定义,可以使用可变关键字传参.
5. 参数解构
def add(x,y):
print (x,y)
return x + y
sum=4,5
add(4,5)
# add((4,5)) 有问题 需要两个参数,但是这里只有一个
add(sum[0],sum[1])
add(*sum)
add(*(4,5))
add(*{
4,5})
add(*[4,5])
add(*range(4,6))
add( *{
'a' : '10' , 'b' : '20'} )
#add( **{'a' : '10' , 'b' : '20'} ) 有问题 会得到意外参数a,机构后 a=10,b=20
add( **{
'x' : '10' , 'y' : '20'} )
总结
解构时*会理解为位置传参,而**会理解为关键字传参随后匹配函数的实参
提取出来的元素回合函数的形参对比
二、函数返回值
returnn
实验返回值返回个数,返回后函数还会继续执行吗,多个返回值是否可以
def add(x,y):
k = x + y
return k
print ("123")
def fn1(x):
print (x)
return x + 1
return x + 1
def fn2(y):
if y = 2 :
return y + 1
elif y < 3 :
return y + 2
add(4,5)
fn1(10)
fn2(2)
#实验一下
总结:
函数都有返回值,函数使用return作为返回值
函数返回值不写,隐式调用return none
return 不一定是函数的最后一句
一个函数可以有多个return 但是只会执行一个,如果没有执行那么是隐式none
当执行return后,后续函数不再执行,
return用来结束函数调用,返回返回值
如果有必要可调用隐式return
三、函数作用域
1. 作用域
标识符可见范围作用域函数中
def foo():
x = 100
print (x)
#会不会访问到? NameError: name 'x' is not defined
不会被访问到,甚至会报错NameError
因为x只定义在foo函数中,而没用定义在全局
2.作用域分类
1.全局作用域
1.整个程序运行环境可见
2.全局作用域变量称之为全局变量global
2.局部作用域
1.在函数,类经常可见,
2.局部作用域中的变量,其使用范围不会超过本函数或类
3.也称之为本地作用域local
#局部变量
def fn0():
x=10
return x
def fn1():
print (x)
print (x)
#name 'x' is not defined
#全局变量
x = 10
def fn0():
return x + 1
fn0()
#返回值???
3. 函数嵌套
def fn0():
def too():
print ('sum')
too()
print ('fn0')
fn0()
too()
#NameError: name 'too' is not defined
内部的函数不知能直接在外部使用,否则会抛出NameError异常,以为他在函数体外并不可见.
其实too就是一个标识符,函数名字而已,一个fn0函数内定义的一个变量而已.
1.嵌套解构的作用域
def other1():
f = 100
def too():
print ('too',f,chr(f))
too()
print ('other',f,chr(f) )
other1()
#other1 返回 too 100
#too 100 d
#other 100 d
def other2():
f = 200
def too():
f = 201
print ('too',f,chr(f))
too()
print ('other2',f,chr(f))
other2()
#other2 返回函数内部调用f = 201
#too 201 É
#other2 200 È
总结: 经此发现内部函数f = 201的作用域只在too函数中,外部是不可调用的.
如果嵌套函数外层定义一个f = 201,内层还有一个相同的变量名称,那么函数有优先调用本函数内的变量.
内建函数 | 函数签名 | 说明 |
---|---|---|
chr | chr(i) | 通过Unicode编码返回对应字符 |
ord | ord(i) | 通过字符获得对应的unicode |
print (ord('中'),hex(ord('中')),'中'.encode(),'中'.encode('gbk'))
#20013 0x4e2d b'\xe4\xb8\xad' b'\xd6\xd0'
chr(20013) #'中'
chr(97) #'a'
4.赋值语句的问题
x = 10 def fn(): print (x) fn() #执行正常