函数、函数参数、参数解构

 

python函数与其它语言不太一样;

 

函数:

数学定义,y=f(x)yx的函数,x是自变量;

python函数,由语句块、函数名称、参数列表、若干语句构成,它是组织代码的最小单元,完成一定的功能;

 

作用:

结构化编程对代码的最基本的封装,一般按照功能组织一段代码;

封装的目的为了复用,减少冗余代码;

代码更加简洁美观、可读易懂(工业化要求);

 

分类:

内建函数built-in,如max(),reversed()等;

库函数,如math.ceil()等、第三方库;

 

函数定义(声明):

def语句定义函数;

def 函数名(参数列表):

         函数体(代码块)

         [return 返回值]

函数名,就是标识符,命名要求一样(见明知义);

语句块必须缩进,约定4个空格;

python的函数没有return语句,隐式会返回一个None值;在一些语言中函数没有返回值称为过程,有返回值称为函数;

函数中若有两个return,只返回第一个return的结果,return a,b返回的是tuple类型;

定义中的参数列表称为形式参数,只是一种符号表达,简称形参,即y=f(x)中的x

定义要在调用前,否则抛NameError异常;

 

函数调用:

函数定义,只是声明了一个函数,它不会被执行,需要调用,不调用的函数与变量一样;

调用的方式,就是函数名加上小括号,括号内写上参数;

调用时写的参数是实际参数,是实实在在传入的值,简称实参;

函数是可调用对象,可用callable()判断;

 

例:

In [1]: def add(x,y):

   ...:     result = x + y

   ...:     return result

   ...:

In [2]: out=add(4,5)

In [3]: out

Out[3]: 9

In [4]: callable(add)

Out[4]: True

 

函数参数:

参数调用时传入的参数要和定义的个数相匹配(可变参数例外,内建函数中列表是作为整体传入的);

位置参数 def f(x,y,z):,调用时使用f(1,3,5),按照参数定义顺序传入实参;

关键字参数(命名参数keyword),def f(x,y,z):,调用时使用f(x=1,y=3,z=5),使用形参的名字来传入实参的方式,如果使用了形参名字,那么传参顺序可和定义顺序不同;

传参,位置参数必须在关键字参数之前传入,位置参数是按位置对应的(简单的往前放,复杂的往后放);

 

例:

In [6]: def f(x,y,z):

   ...:     print(x,y,z)

   ...:    

In [7]: f(z=None,y=10,x=[1])

[1] 10 None

In [8]: f((1,),z=6,y=4.1)

(1,) 4.1 6

In [9]: f(y=5,z=6,2)   #错误,位置参数必须在关键字参数之前传入

  File "", line 1

    f(y=5,z=6,2)

             ^

SyntaxError: positional argument follows keyword argument

 

函数参数默认值

定义时,在形参后赋一个值;

作用:参数的默认值,可在未传入足够的实参的时候,对没有给定的参数赋值为默认值;参数非常多的时候,并不需要用户每次都输入所有的参数,简化函数调用;

调用时必须要传的参数,在定义时写在前面;

调用时可用默认值的参数,在定义时写在后面;

定义函数时,参数超过2个,都开始用缺省值参数,把重要的参数往前放;

 

例:

In [1]: def add(x=4,y=5):   #xy类型不一样时会异常

   ...:     return x+y

   ...:

In [2]: add()

Out[2]: 9

In [3]: add(6,10)

Out[3]: 16

In [4]: add(6,y=7)

Out[4]: 13

In [5]: add(x=5)

Out[5]: 10

In [6]: add(y=7)

Out[6]: 11

In [7]: add(x=5,6)

  File "", line 1

    add(x=5,6)

           ^

SyntaxError: positional argument follows keyword argument

In [8]: add(y=8,4)

  File "", line 1

    add(y=8,4)

           ^

SyntaxError: positional argument follows keyword argument

In [9]: add(x=5,y=6)

Out[9]: 11

In [9]: add(x=5,y=6)

Out[9]: 11

In [10]: add(y=6,x=5)

Out[10]: 11

In [11]: def add(x=4,y):   #缺省值要往后放

    ...:     pass

  File "", line 1

    def add(x=4,y):

           ^

SyntaxError: non-default argument follows default argument

In [12]: def add(x,y=5):

    ...:     return x+y

    ...:

In [13]: add()

---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

in ()

----> 1 add()

TypeError: add() missing 1 required positional argument: 'x'

In [14]: add(10)

Out[14]: 15

 

例:

定义一个函数login,参数名称为host,port,username,password

In [17]: def login(host='127.0.0.1',port='8080',username='jowin',password='chai'):

    ...:     print('{}:{}@{}/{}'.format(host,port,username,password))

    ...:    

In [18]: login()

127.0.0.1:8080@jowin/chai

In [19]: login('127.0.0.1',80,'jerry','jerry')

127.0.0.1:80@jerry/jerry

In [20]: login('localhost',port=80,password='test')

localhost:80@jowin/test

In [21]: login(port=80,password='test',host='localhost')

localhost:80@jowin/test

 

可变参数:

一个形参可以匹配任意个(包括0个)参数(实参);

 

位置可变参数

在形参前使用*,表示该形参是可变参数,可以接收多个实参,函数名约定用*args

收集多个实参为一个tuple

关键字参数也能传递任意多个吗?不能

 

关键字可变参数

形参前使用**,表示可接收多个关键字参数,参数名约定用**kwargs

收集的实参名称和值组成一个字典;

 

可变参数总结:

有位置可变参数和关键字可变参数;

位置可变参数在形参前使用*

关键字可变参数在形参前使用**

位置可变参数和关键字可变参数都可收集若干个实参,位置可变参数收集形成一个tuple,关键字可变参数收集形成一个dict

混合使用参数时,可变参数要放到参数列表的最后(普通参数需放到参数列表前面),位置可变参数需要在关键字可变参数之前;

 

keyword-only参数

python3加入;

如果在一个星号参数后,或者一个位置可变参数后,出现的普通参数,实际上已经不是普通参数了,而是keyword-only参数;

使用方式一,def fn(*args,x):def fn(*args,x,y,**kwargs):

使用方式二,def fn(*,x,y):def fn(z,*,x,y),只允许x,y两个参数,且这两个参数只能是keyword-only参数,*之后,普通形参都变成了必须给出的keyword-only参数;

def fn(**kwargs,x):SyntaxError**kwargs后不需要再跟keyword,多此一举,可理解为**kwargs会截取所有的关键字参数,就算有x=5x也永远得不到这个值,所以语法错误;

 

例:

In [22]: def add(nums):   #需求,多个数累加求和

    ...:     sum=0

    ...:     for i in nums:

    ...:         sum+=i

    ...:     return sum

    ...:

In [23]: add([1,3,5])   #传入一个可迭代对象,迭代元素求和

Out[23]: 9

In [24]: add((2,4,6))

Out[24]: 12

In [27]: def add(*args):

    ...:     sum=0

    ...:     print(type(args))   #tuple,传进后是不可变类型

    ...:     for i in args:

    ...:         sum+=i

    ...:     print(sum)

    ...:     return sum

    ...:

In [28]: add(3,6,7)

16

Out[28]: 16

In [29]: val=add(3,5,7)   #函数中若无returnval的值为None;不认关键字参数

15

In [30]: val

Out[30]: 15

 

例:

In [31]: def showconfig(**kwargs):

    ...:     print(type(kwargs))

    ...:     for k,v in kwargs.items():   #dict.items()python3中叫dictionary view

    ...:         print('{}={}'.format(k,v))

    ...:        

In [32]: showconfig(host='localhost',port=8080,username='joiwn',password='chai')

password=chai

port=8080

username=joiwn

host=localhost

 

可变参数混合使用:

def showconfig(username,password,**kwargs):

def showconfig(username,*args,**kwargs):

def showconfig(username,password,**kwargs,*args):   #错误,位置可变参数需在关键字可变参数之前

 

例:

In [33]: def fn(x,y,*args,**kwargs):

    ...:     print(x)

    ...:     print(y)

    ...:     print(args)

    ...:     print(kwargs)

    ...:    

In [34]: fn(3,5,7,9,10,a=1,b='python')

3

5

(7, 9, 10)

{'a': 1, 'b': 'python'}

In [35]: fn(3,5)

3

5

()

{}

In [36]: fn(3,5,7)

3

5

(7,)

{}

In [37]: fn(3,5,a=1,b='python')

3

5

()

{'a': 1, 'b': 'python'}

In [38]: fn(7,9,y=5,x=3,a=1,b='python')   #y=5,x=3与前面的7,9重复了,multiple values

---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

in ()

----> 1 fn(7,9,y=5,x=3,a=1,b='python')

TypeError: fn() got multiple values for argument 'y'

 

例:

In [39]: def fn(*args,x,y,**kwargs):   #此种方式定义属keyword-only,在调用传参时x,y要用关键字参数形式,要先满足keyword-only,即x,y,其它的才有着落

    ...:     print(args)

    ...:     print(x)

    ...:     print(y)

    ...:     print(kwargs)

    ...:    

In [40]: fn(7,9,y=5,x=3,a=1,b='python')   #*args后的x,ykeyword-only参数,要先满足它俩

(7, 9)

3

5

{'a': 1, 'b': 'python'}

In [41]: fn(3,5)

---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

in ()

----> 1 fn(3,5)

TypeError: fn() missing 2 required keyword-only arguments: 'x' and 'y'

In [42]: fn(3,5,7)

---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

in ()

----> 1 fn(3,5,7)

TypeError: fn() missing 2 required keyword-only arguments: 'x' and 'y'

In [43]: fn(3,5,a=1,b='python')

---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

in ()

----> 1 fn(3,5,a=1,b='python')

TypeError: fn() missing 2 required keyword-only arguments: 'x' and 'y'

 

例:

In [44]: def fn(*args,x):   #args可以看作已经截获了所有的位置参数,x不使用关键字参数就不可能拿到实参

    ...:     print(args)

    ...:     print(x)

    ...:    

In [45]: fn(3,5,x=7)   #fn(3,5)fn(3,5,7)均是错的

(3, 5)

7

 

例:

In [47]: def fn(*,x,y):

    ...:     print(x,y)

    ...:    

In [48]: fn(x=1,y=2)   #fn(4,5)fn(6,x=1,y=2)错误

1 2

In [49]: def fn(z,*,x,y):   #x,y必须是keyword-only参数,z不作要求

    ...:     print(x,y)

    ...:    

In [50]: fn(1,x=2,y=3)

2 3

 

例(可变参数和参数默认值):

In [1]: def fn(*args,x=5):

   ...:     print(args)

   ...:     print(x)

   ...:    

In [2]: fn()

()

5

In [3]: fn(5)   #25不一样

(5,)

5

In [4]: fn(x=6)

()

6

In [5]: def fn(y,*args,x=5):

   ...:     print('x={},y={}'.format(x,y))

   ...:     print(args)

   ...:    

In [6]: fn(5)   #fn()fn(x=6)错误,至少要给y一个位置参数

x=5,y=5

()

In [7]: fn(y=17,2,3,x=10)   #关键字参数一定要放到位置参数后面

  File "", line 1

    fn(y=17,2,3,x=10)

           ^

SyntaxError: positional argument follows keyword argument

In [8]: fn(1,2,y=3,x=10)   #y重复了

---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

in ()

----> 1 fn(1,2,y=3,x=10)

TypeError: fn() got multiple values for argument 'y'

In [9]: fn(1,2,3,x=10)

x=10,y=1

(2, 3)

In [10]: def fn(x=5,**kwargs):

    ...:     print('x={}'.format(x))

    ...:     print(kwargs)

    ...:    

In [11]: fn()

x=5

{}

In [12]: fn(5)

x=5

{}

In [13]: fn(x=6)

x=6

{}

In [14]: fn(y=3,x=10)

x=10

{'y': 3}

In [15]: fn(3,y=10)

x=3

{'y': 10}

 

参数规则:

参数列表一般顺序是,普通参数-->缺省参数-->可变位置参数-->keyword-only参数(可带缺省值)-->可变关键字参数

注:

代码应该易读易懂,而不是为难别人;

要按照书面习惯,定义函数;

 

例:

In [16]: def fn(x,y,z=3,*args,m=4,n,**kwargs):   #def fn(x,y,z=3,*,n,m=4,**kwargs):这样最好

    ...:     print(x,y,z,m,n)

    ...:     print(args)

    ...:     print(kwargs)

    ...:    

In [17]: fn(1,2,n=3)

1 2 3 4 3

()

{}

In [18]: fn(1,2,10,11,t=7,n=5)

1 2 10 4 5

(11,)

{'t': 7}

In [19]: fn(1,2,10,11,n=5,t=100)

1 2 10 4 5

(11,)

{'t': 100}

In [20]: fn(1,2,10,11,x=3,n=4)   #x重复

---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

in ()

----> 1 fn(1,2,10,11,x=3,n=4)

TypeError: fn() got multiple values for argument 'x'

 

例:

In [22]: def connect(host='localhost',port='3306',user='admin',passwd='admin',**kwargs):

    ...:     print(host,port)

    ...:     print(user,passwd)

    ...:     print(kwargs)

    ...:    

In [23]: connect(db='cmdb')

localhost 3306

admin admin

{'db': 'cmdb'}

In [24]: connect(host='192.168.7.47',db='cmdb')

192.168.7.47 3306

admin admin

{'db': 'cmdb'}

In [25]: connect(host='192.168.7.47',db='cmdb',passwd='myadmin')

192.168.7.47 3306

admin myadmin

{'db': 'cmdb'}

 

 

 

参数解构:

给函数提供实参的时候,可在集合类型前使用***,把集合类型的结构解开,提取出所有元素作为函数的实参;

非字典类型使用*,解构成位置参数;

字典类型使用**,解构成关键字参数;

提取出来的元素数目要和参数的要求匹配,也要和参数的类型匹配;

 

例:

In [26]: def add(x,y):

    ...:     return x+y

    ...:

In [27]: add(4,5)

Out[27]: 9

In [28]: add((4,5))

---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

in ()

----> 1 add((4,5))

TypeError: add() missing 1 required positional argument: 'y'

In [29]: t=(4,5)

In [30]: add(t[0],t[1])

Out[30]: 9

In [31]: add(*t)

Out[31]: 9

In [32]: add(*range(4,6))

Out[32]: 9

 

例:

In [1]: def add(x,y):

   ...:     return x+y

   ...:

In [2]: d={'x':5,'y':6}

In [3]: add(**d)

Out[3]: 11

In [4]: add(**{'a':5,'b':6})   #关键字参数中没有a,b

---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

in ()

----> 1 add(**{'a':5,'b':6})

TypeError: add() got an unexpected keyword argument 'a'

In [6]: add(*d.keys())

Out[6]: 'yx'

In [7]: add(*d.values())

Out[7]: 11

 

参数解构和可变参数:

例:

In [8]: def add(*iterable):

   ...:     result=0

   ...:     for i in iterable:

   ...:         result+=i

   ...:     return result

   ...:

In [9]: add(1,2,3)

Out[9]: 6

In [10]: add(*[1,2,3])   #常用

Out[10]: 6

In [11]: add(*range(10))

Out[11]: 45

 

例:

In [13]: def fn(a,b):

    ...:     return a,b

    ...:

In [14]: type(fn(4,5))   #return a,b返回的tuple类型,若函数中有多个return语句,只返回第一个return的结果

Out[14]: tuple

 

习题:

1、编写一个函数,能接受至少2个参数,返回最小值和最大值?

def double_values(*args):

    print(args)

    return max(args),min(args)

 

fn(3,2,22,2,1)

#####################

import random

 

def double_values(*args):

    print(args)

    return max(args),min(args)

 

print(*double_values(*[random.randint(10,20) for _ in range(10)]))   #两处解构,double_values前面若无*则打印tupleprint()函数有隐式转换str(),即使是print(1,2)也会把1,2转为string类型再打印

 

2、编写一个函数,接受一个参数nn为正整数),左右两种打印方式,要求数字必须对齐?

10函数_函数参数_第1张图片

################################

10函数_函数参数_第2张图片

#############################

方一:

lst = [str(i) for i in range(12,0,-1)]

strs = ' '.join(lst)

length = len(strs)

 

print(strs,length,sep='\n')

 

for i in range(1,12):

    print('{:>{}}'.format(' '.join([str(j) for j in range(i,0,-1)]),length))

print(strs)

#################

lst = [str(i) for i in range(12,0,-1)]

strs = ' '.join(lst)

length = len(strs)

 

for col in range(1,12):

    row = ' '.join([str(j) for j in range(col,0,-1)])

    #print(row)

    print('{}{}'.format(' '*(length-len(row)),row))

print(strs)

#################

def show(num):

    tail = ' '.join([str(i) for i in range(num,0,-1)])

    width = len(tail)

    for i in range(1,num):

        print('{:>{}}'.format(' '.join([str(j) for j in range(i,0,-1)]),width))

    print(tail)

 

show(12)

#################

lst = [str(i) for i in range(12,0,-1)]

strs = ' '.join(lst)

length = len(strs)

 

print(strs)

for col in range(11,0,-1):

    row = ' '.join([str(j) for j in range(col,0,-1)])

    #print(row)

    print('{}{}'.format(' '*(length-len(row)),row))

####################

方二:

def show(num):

    tail = ' '.join([str(i) for i in range(num,0,-1)])

    print(tail)

    for i in range(len(tail)):   #无需再次生成列表

        if tail[i] == ' ':

            print(' '*i,tail[i+1:])

 

show(15)

#####################

方三:

def show(num):

    for i in range(1,num+1):

        for j in range(num,0,-1):

            if i < j:

                print(' '*len(str(j)),end=' ')

            else:

                print(j,end=' ')

        print()

 

show(15)

#########################