美文网首页python热爱者Python新世界
python函数基础与python函数的参数细节!

python函数基础与python函数的参数细节!

作者: 48e0a32026ae | 来源:发表于2018-11-10 16:30 被阅读1次

    函数基础(私信小编007即可自动获取大量python视频教程以及各类PDF!)

    函数简介

    定义:就是具有特定功能的一段代码

    优点:

    解决代码的重复书写

    可以将功能的实现着和使用者分开,提高开发效率

    学习Python中有不明白推荐加入交流群

                    号:516107834

                    群里有志同道合的小伙伴,互帮互助,

                    群里有不错的学习教程!

    分类:

    库函数:print、input、abs等

    自定义:用户自己封装的函数

    函数使用

    定义函数格式

    def 函数名([参数列表]):

    函数体

    []表示可选,参数可以有,也可以没有,也可以是多个。

    函数名:

    与变量一样,只要遵循标识符的命名规范即可。

    函数调用:

    函数定以后不会执行,需要调用才会执行

    调用格式: 函数名([参数])

    函数分类(参数与返回值)

    # 无参无返回值

    def print_hello():

    for i in range(10):

    print('Hello world!')

    # print_hello()

    # 带参数无返回值

    def print_n_hello(n):

    for i in range(n):

    print('Hello world!')

    # print_n_hello(5)

    # 带参有返回值

    def add(m, n):

    c = m + n

    # 使用return返回需要返回的值,不返回默认为None

    return c

    print(add(3, 5))

    分类的标准是按照有无参数,以及有无返回值。

    函数参数

    形参:形式参数,就是写在函数定义出的参数

    实参:实际参数,就是在函数调用时传递的参数

    位置参数:也叫必传参数,没有默认值的参数

    默认参数:定义时有默认值,调用时可以不传,必须写在最后

    关键字参数:在函数调用时指定参数名字,参数的位置就无所谓。

    可变长度参数:函数调用时可以传递比定义处更多的参数,多出来的参数会保存在args和kwargs中

    def var_len_args(a, b, name='goudan', *args, **kwargs):

    print(a, b)

    print(name)

    # 是一个元组,保存多传的位置参数

    print(args)

    # 是一个字典,保存多传的关键字参数

    print(kwargs)

    var_len_args(1, 2, 3, 4, age=10)

    *的使用:

    def show(a, b):

    print(a, b)

    lt = [1, 2]

    # 需要从列表根据下表获取元素,然后作为参数传递

    # show(lt[0], lt[1])

    show(*lt)

    def show2(aa=11, bb=22):

    print(aa, bb)

    d = {'aa': 123, 'bb': 456}

    # show2(aa=d['aa'], bb=d['bb'])

    # 上下等价

    show2(**d)

    函数进阶

    函数的定义位置是随意的?

    不是,一定是先定义后调用,函数的调用一定要放在定义之后。

    函数的名字可以相同吗?

    不可以,若出现同名函数,后者会覆盖前者。

    函数内容能否再定义函数?

    可以,定义在函数内部的函数叫内部函数,它是很多高级功能实现的基础。

    变量的作用域

    块级作用域

    if True:

    name = 'ergou'

    # 能够在块的外部使用,说明没有块级作用域

    print(name)

    局部作用域

    局部变量:定义在函数内部的变量叫局部变量,只能在函数内部使用。

    def test():

    a = 10

    # 不能再函数外部使用,此处会报错

    print(a)

    全局作用域

    全局变量:定义在函数外部的变量叫全局变量,哪里都可以使用

    num = 10

    def show():

    # 声明使用的时外部的同名变量

    global num

    # 只能使用,不能修改,若想修改协议进行全局声明

    num = 20

    print(num)

    show()

    print(num)

    函数内部可以使用外部变量,但是不能修改

    若修改需要使用 global 进行全局声明

    当函数内部与外部出现同名变量,默认使用的时内部变量

    nonlocal使用

    说明:内部函数使用外部函数的局部变量,需要使用nonlocal进行声明

    def wai():

    n = 10

    def nei():

    # 声明使用非本地变量,外部函数的局部变量

    nonlocal n

    n = 20

    print(n)

    nei()

    print(n)

    wai()

    递归函数(了解)

    定义:函数内部调用函数本身的函数叫递归函数。

    组成:

    函数内部调用自己

    终止条件(停止调用自己的条件)

    核心算法(具体功能)

    特点:

    代码简洁

    可读性差(不易理解)

    瞬间占用内存较大,没有终止条件立即崩溃

    有些领域是禁止使用的(安全领域:汽车电子)

    只有在不得不使用的时候才使用(遍历目录)

    练习:

    求n的阶乘

    斐波那契数列的第n项

    前两项都为1,从第三项开始,每一项都等于前两项的和

    如: 1,1,2,3,5,8,13,21,...

    常用函数

    内置函数(无需导入)

    print:打印,输出

    input:输入

    abs:求绝对值

    type:获取变量的类型

    chr:将ASCII数值转换为字符

    ord:将字符转换为对应的ASCII

    id:获取变量地址

    len:统计元素个数

    range:产生连续整数的可迭代对象

    enumerate:枚举可迭代对象

    强制类型转换:

    int

    float

    str

    list

    tuple

    set

    dict

    max:求多个参数的最大值,或可迭代对象中的最大元素

    min:最小值

    sum:求和,可迭代对象元素求和

    pow:求幂,pow(2, 3)等价于2 ** 3

    round:四舍五入,可以指定保留位数

    hex:16进制

    oct:8进制

    bin:2进制

    模块函数(需要导入)

    time:

    sleep:休眠指定的秒数,可以是小数

    math:

    e:自然对数

    pi:圆周率

    ceil:向上取整

    floor:向下取整

    sqrt:开方(请平方根)

    degrees:弧度转换为度

    radians:度转换为弧度

    sys:

    argv:运行程序时所有的命令行参数列表,第一个是程序文件名

    练习:完成一个计算器,支持加、减、乘、除运算

    练习:

    前面练习全部以函数形式进行封装

    终端计算器

    删除列表中的重复元素

    统计一个字符串中字母、数字及其他字符的个数,返回一个元组即可

    返回一个列表中第二大的数

    完整列表的排序函数(sort),支持逆序

    号码归属地查询:

    '''5582|1860101|010|北京市|北京联通GSM卡

    5583|1860100|010|北京市|北京联通GSM卡

    5584|1368141|010|北京市|北京移动神州行卡

    5585|1860111|010|北京市|北京联通GSM卡

    5586|1358198|010|北京市|北京移动动感地带卡

    5587|1361139|010|北京市|北京移动预付费卡

    5588|1361138|010|北京市|北京移动神州行卡

    5591|1360110|010|北京市|北京移动全球通卡

    5748|1364110|010|北京市|北京移动神州行卡

    10186|1581584|020|广东省广州市|广东移动全球通卡

    15046|1391897|021|上海市|上海移动全球通卡

    17250|1502207|022|天津市|天津移动全球通卡

    21137|1345272|023|重庆市万州|重庆移动大众卡

    22700|1347812|024|辽宁省沈阳市|辽宁移动大众卡

    24256|1377065|025|江苏省南京市|江苏移动全球通卡

    26360|1898606|027|湖北省武汉市|湖北电信CDMA卡

    28709|1860802|028|四川省成都市|四川联通GSM卡

    30641|1552961|029|陕西省西安市|陕西联通GSM卡

    31700|1563007|0310|河北省邯郸市|河北联通GSM卡

    33360|1583396|0311|河北省石家庄市|河北移动全球通卡

    34825|1508122|0312|河北省保定市|河北移动全球通卡

    35363|1551235|0313|河北省张家口|河北联通GSM卡

    37700|1331326|0316|河北省廊坊市|河北电信CDMA卡

    43500|1350358|0358|山西省吕梁市|山西移动全球通卡

    43908|1553625|0359|山西省运城市|山西联通GSM卡

    44521|1335360|0370|河南省商丘市|河南电信CDMA卡

    50078|1509369|0378|河南省开封市|河南移动全球通卡

    53603|1583981|0398|河南省三门峡|河南移动全球通卡

    53916|1335897|0410|辽宁省铁岭市|辽宁电信CDMA卡

    55248|1554254|0411|辽宁省大连市|辽宁联通GSM卡

    58618|1374272|0427|辽宁省盘锦市|辽宁移动全球通卡

    58932|1554183|0429|辽宁省葫芦岛|辽宁联通GSM卡

    60268|1340475|0431|吉林省长春市|吉林移动大众卡'''

    函数基础

    函数简介

    定义:就是具有特定功能的一段代码

    优点:

    解决代码的重复书写

    可以将功能的实现着和使用者分开,提高开发效率

    分类:

    库函数:print、input、abs等

    自定义:用户自己封装的函数

    函数使用

    定义函数格式

    def 函数名([参数列表]):

    函数体

    []表示可选,参数可以有,也可以没有,也可以是多个。

    函数名:

    与变量一样,只要遵循标识符的命名规范即可。

    函数调用:

    函数定以后不会执行,需要调用才会执行

    调用格式: 函数名([参数])

    函数分类(参数与返回值)

    # 无参无返回值

    def print_hello():

    for i in range(10):

    print('Hello world!')

    # print_hello()

    # 带参数无返回值

    def print_n_hello(n):

    for i in range(n):

    print('Hello world!')

    # print_n_hello(5)

    # 带参有返回值

    def add(m, n):

    c = m + n

    # 使用return返回需要返回的值,不返回默认为None

    return c

    print(add(3, 5))

    分类的标准是按照有无参数,以及有无返回值。

    函数参数

    形参:形式参数,就是写在函数定义出的参数

    实参:实际参数,就是在函数调用时传递的参数

    位置参数:也叫必传参数,没有默认值的参数

    默认参数:定义时有默认值,调用时可以不传,必须写在最后

    关键字参数:在函数调用时指定参数名字,参数的位置就无所谓。

    可变长度参数:函数调用时可以传递比定义处更多的参数,多出来的参数会保存在args和kwargs中

    def var_len_args(a, b, name='goudan', *args, **kwargs):

    print(a, b)

    print(name)

    # 是一个元组,保存多传的位置参数

    print(args)

    # 是一个字典,保存多传的关键字参数

    print(kwargs)

    var_len_args(1, 2, 3, 4, age=10)

    *的使用:

    def show(a, b):

    print(a, b)

    lt = [1, 2]

    # 需要从列表根据下表获取元素,然后作为参数传递

    # show(lt[0], lt[1])

    show(*lt)

    def show2(aa=11, bb=22):

    print(aa, bb)

    d = {'aa': 123, 'bb': 456}

    # show2(aa=d['aa'], bb=d['bb'])

    # 上下等价

    show2(**d)

    函数进阶

    函数的定义位置是随意的?

    不是,一定是先定义后调用,函数的调用一定要放在定义之后。

    函数的名字可以相同吗?

    不可以,若出现同名函数,后者会覆盖前者。

    函数内容能否再定义函数?

    可以,定义在函数内部的函数叫内部函数,它是很多高级功能实现的基础。

    变量的作用域

    块级作用域

    if True:

    name = 'ergou'

    # 能够在块的外部使用,说明没有块级作用域

    print(name)

    局部作用域

    局部变量:定义在函数内部的变量叫局部变量,只能在函数内部使用。

    def test():

    a = 10

    # 不能再函数外部使用,此处会报错

    print(a)

    全局作用域

    全局变量:定义在函数外部的变量叫全局变量,哪里都可以使用

    num = 10

    def show():

    # 声明使用的时外部的同名变量

    global num

    # 只能使用,不能修改,若想修改协议进行全局声明

    num = 20

    print(num)

    show()

    print(num)

    函数内部可以使用外部变量,但是不能修改

    若修改需要使用 global 进行全局声明

    当函数内部与外部出现同名变量,默认使用的时内部变量

    nonlocal使用

    说明:内部函数使用外部函数的局部变量,需要使用nonlocal进行声明

    def wai():

    n = 10

    def nei():

    # 声明使用非本地变量,外部函数的局部变量

    nonlocal n

    n = 20

    print(n)

    nei()

    print(n)

    wai()

    递归函数(了解)

    定义:函数内部调用函数本身的函数叫递归函数。

    组成:

    函数内部调用自己

    终止条件(停止调用自己的条件)

    核心算法(具体功能)

    特点:

    代码简洁

    可读性差(不易理解)

    瞬间占用内存较大,没有终止条件立即崩溃

    有些领域是禁止使用的(安全领域:汽车电子)

    只有在不得不使用的时候才使用(遍历目录)

    练习:

    求n的阶乘

    斐波那契数列的第n项

    前两项都为1,从第三项开始,每一项都等于前两项的和

    如: 1,1,2,3,5,8,13,21,...

    常用函数

    内置函数(无需导入)

    print:打印,输出

    input:输入

    abs:求绝对值

    type:获取变量的类型

    chr:将ASCII数值转换为字符

    ord:将字符转换为对应的ASCII

    id:获取变量地址

    len:统计元素个数

    range:产生连续整数的可迭代对象

    enumerate:枚举可迭代对象

    强制类型转换:

    int

    float

    str

    list

    tuple

    set

    dict

    max:求多个参数的最大值,或可迭代对象中的最大元素

    min:最小值

    sum:求和,可迭代对象元素求和

    pow:求幂,pow(2, 3)等价于2 ** 3

    round:四舍五入,可以指定保留位数

    hex:16进制

    oct:8进制

    bin:2进制

    模块函数(需要导入)

    time:

    sleep:休眠指定的秒数,可以是小数

    math:

    e:自然对数

    pi:圆周率

    ceil:向上取整

    floor:向下取整

    sqrt:开方(请平方根)

    degrees:弧度转换为度

    radians:度转换为弧度

    sys:

    argv:运行程序时所有的命令行参数列表,第一个是程序文件名

    练习:完成一个计算器,支持加、减、乘、除运算

    练习:

    前面练习全部以函数形式进行封装

    终端计算器

    删除列表中的重复元素

    统计一个字符串中字母、数字及其他字符的个数,返回一个元组即可

    返回一个列表中第二大的数

    完整列表的排序函数(sort),支持逆序

    号码归属地查询:

    '''5582|1860101|010|北京市|北京联通GSM卡

    5583|1860100|010|北京市|北京联通GSM卡

    5584|1368141|010|北京市|北京移动神州行卡

    5585|1860111|010|北京市|北京联通GSM卡

    5586|1358198|010|北京市|北京移动动感地带卡

    5587|1361139|010|北京市|北京移动预付费卡

    5588|1361138|010|北京市|北京移动神州行卡

    5591|1360110|010|北京市|北京移动全球通卡

    5748|1364110|010|北京市|北京移动神州行卡

    10186|1581584|020|广东省广州市|广东移动全球通卡

    15046|1391897|021|上海市|上海移动全球通卡

    17250|1502207|022|天津市|天津移动全球通卡

    21137|1345272|023|重庆市万州|重庆移动大众卡

    22700|1347812|024|辽宁省沈阳市|辽宁移动大众卡

    24256|1377065|025|江苏省南京市|江苏移动全球通卡

    26360|1898606|027|湖北省武汉市|湖北电信CDMA卡

    28709|1860802|028|四川省成都市|四川联通GSM卡

    30641|1552961|029|陕西省西安市|陕西联通GSM卡

    31700|1563007|0310|河北省邯郸市|河北联通GSM卡

    33360|1583396|0311|河北省石家庄市|河北移动全球通卡

    34825|1508122|0312|河北省保定市|河北移动全球通卡

    35363|1551235|0313|河北省张家口|河北联通GSM卡

    37700|1331326|0316|河北省廊坊市|河北电信CDMA卡

    43500|1350358|0358|山西省吕梁市|山西移动全球通卡

    43908|1553625|0359|山西省运城市|山西联通GSM卡

    44521|1335360|0370|河南省商丘市|河南电信CDMA卡

    50078|1509369|0378|河南省开封市|河南移动全球通卡

    53603|1583981|0398|河南省三门峡|河南移动全球通卡

    53916|1335897|0410|辽宁省铁岭市|辽宁电信CDMA卡

    55248|1554254|0411|辽宁省大连市|辽宁联通GSM卡

    58618|1374272|0427|辽宁省盘锦市|辽宁移动全球通卡

    58932|1554183|0429|辽宁省葫芦岛|辽宁联通GSM卡

    60268|1340475|0431|吉林省长春市|吉林移动大众卡'''

    按"指针"传递

    python中 变量赋值、参数传递都是通过"指针"拷贝的方式进行的 。除了按"指针"拷贝,还有一种按值拷贝的方式,关于按值、按指针拷贝的细节,参见 按值传递 vs. 按指针传递 。

    所以在python中,变量赋值、参数传递,都只是拷贝了源数据的一个地址,而不会拷贝内存中完整的数据对象副本。所以,如果在函数内部修改变量指向的数据对象,会影响函数外部的数据。

    例如:

    def f(x):

    print(x+3)

    a=4

    f(a)

    在将 a 赋值给本地变量x的时候,只是拷贝了a目前保存的地址给x,使得x也保存了内存中数据对象4的地址。

    如果传递的数据对象是可变数据对象(例如列表),那么在函数内部修改它,会影响函数外部的原始数据对象:

    L1=[11,22,33,44]

    def f(x):

    x[0] += 1

    f(L1)

    print(L1) # 输出:[12, 22, 33, 44]

    显然,在函数内部修改 x[0] 的值,函数外部原始的L1也发生了改变。因为L1赋值给x的时候,只是拷贝了一份L1所指向列表的地址给x,使得x也指向这个列表。

    为了避免这种情况,可以新创建一份列表的副本,然后传递给函数参数。

    L1=[11,22,33,44]

    def f(x):

    x[0] += 1

    f(L1[:])

    print(L1) # 输出:[11, 22, 33, 44]

    上面传递给函数参数x的是 L1[:] ,它会在内存中创建一个新的列表副本,所以x指向的是这个新的副本列表,修改它不会影响原始的列表L1。

    函数参数

    Python的函数对参数和返回值方面非常宽松,参数变量可以是任意数据类型,返回值也一样,只需使用变量名代替它们即可。

    例如,下面的参数x可以是任意类型的结构,可以是数值、字符串、列表、字典等等类型。返回值语句return同理。

    def f(x):

    print(x)

    return x

    f(2)

    f("haha")

    实际上,上面调用函数时是按照参数位置进行传参对本地变量x进行赋值的。除此之外,还可以指定为 key=value 的方式进行传参。例如:

    f(x=2)

    f(x="haha")

    按位置传参

    如果是多个参数,则按从左到右的顺序进行参数变量的赋值:

    def f(x,y,z):

    print(x)

    print(y)

    print(z)

    f(2,3,4)

    调用 f(2,3,4) 的时候,会按照从左向右的位置方式对本地变量x、y、z赋值: x=2,y=3,z=4。

    按关键字key/value方式传值

    python还支持 key=value 的方式设置函数调用时的参数,使用 key=value 的方式赋值时,顺序不重要。这种函数调用时的传值方式称为"关键字传值"。

    例如:

    def f(x,y,z):

    print(x)

    print(y)

    print(z)

    f(x=3,y="haha",z=4)

    也可以打乱顺序:

    f(x=3,z=4,y="haha")

    还可以将 key=value 和位置传参的方式进行混合:

    f(3,"haha",z=4)

    但混合按位置传参方式的时候, 位置参数必须在其它传参方式的前面 ,不仅此处结合 key=value 时如此,后文中 位置参数结合其它方式传参也都如此:位置参数必须在最前面 。

    例如,下面的传参方式是错的:

    f(z=4,3,"haha")

    参数默认值

    在def或lambda声明函数的时候,可以通过 var=default 的方式指定参数的默认值。

    例如:

    def f(x=3):

    print(x)

    f(4)

    f("haha")

    f()

    上面的 f(4) 和 f("haha") 都对函数f()的本地变量x进行了赋值。但是最后一个调用语句 f()未赋值,而是使用参数的默认值3。

    设置参数默认值时,如果函数有多个参数,则带默认值参数后面必须放在最后面。例如:

    # 正确

    def f(x,y,z=4)

    def f(x,y=1,z=4)

    # 错误

    def f(x,y=4,z)

    只要为参数设置了默认值,那么调用函数的时候,这个参数就是可选的,可有可无的,如果没有,则采用默认值。

    def f(x,y=2,z=4):

    print(x)

    print(y)

    print(z)

    # 不采用任何默认值

    f(2,3,4)

    # 采用z的默认值

    f(2,3)

    # 采用y的默认值

    # 此时z必须按key=value的方式传值

    f(2,z=5)

    # y、z都采用默认值

    f(2)

    变长参数:*

    对于任意长度的参数,可以在 def 声明的函数中使用 * 将各 位置参数 收集到一个元组中。例如:

    def f(*args):

    print(args)

    f(1,2,3,4)

    上面调用 f(1,2,3,4) 的时候,将所有参数都收集到了一个名为args的元组中。所以上面的函数将输出:

    (1, 2, 3, 4)

    既然是元组,就可以对参数进行迭代遍历:

    def f(*args):

    for arg in args:

    print(arg)

    f(1,2,3,4)

    必须注意, * 是按位置收集参数的。

    def f(x,y,*args):

    print(x)

    print(y)

    for arg in args:

    print(arg)

    f(1,2,3,4)

    按照从左向右的传参规则,首先将1赋值给x,将2赋值给y,然后将剩余所有的 位置参数 收集到args元组中,所以 args=(3,4) 。

    如果 * 后面还有参数,则调用函数的时候,后面的参数必须使用 key=value 的方式传递,否则会收集到元组中,从而导致参数缺少的问题:

    def f(x,*args,y):

    print(x)

    print(y)

    for arg in args:

    print(arg)

    # 正确

    f(1,3,4,y=2)

    # 错误

    f(1,2,3,4)

    上面调用 f(1,3,4,y=2) 的时候,会按照位置参数对x赋值为1,然后将所有位置参数收集到元组args中,因为 y=2 是非位置参数传值方式,所以 args=(3,4) 。

    如果为上面的y设置默认值:

    def f(x,*args,y=2)

    那么 f(1,2,3,4) 会将 (2,3,4) 都收集到元组args中,然后y采用默认值2。

    变长参数:**

    除了可以使用 * 将位置参数收集到元组中,还可以使用 ** 将 key=value 格式的参数收集到字典中。

    例如:

    def f(x,**args):

    print(x)

    print(args)

    f(1,a=11,b=22,c=33,d=44)

    上面首先按位置传参的方式赋值 x=1 ,然后将剩余的所有 key=value 参数收集到名为args的字典中。所以,args字典的内容为:

    {'a': 11, 'b': 22, 'c': 33, 'd': 44}

    既然是将参数收集到字典中,就可以使用字典类的工具操作这个字典。例如,遍历字典。

    在 ** 的 后面不能出现任何其它类型的参数 。例如,下面的都是错误的def定义方式:

    def f(x,**args,y)

    def f(x,**args,y=3)

    def f(x,**args,*t)

    只能将位置参数或者 * 的收集放在 ** 的前面。

    def f(x,y,**args)

    def f(x,*args1,**args2)

    函数调用时的*和**

    除了在def定义函数时,参数中可以使用 * 或 ** 收集参数,在 函数调用 的时候也可以使用 *或 ** 分别解包元组(列表或其它对象)、字典。一定要注意区分函数定义和函数调用时的 * 、 ** ,它们的用法是不通用的。

    例如,解包元组:

    def f(a,b,c,d):

    print(a)

    print(b)

    print(c)

    print(d)

    T=(1,2,3,4)

    f(*T)

    * 除了可以解包元组,还可以解包其它可迭代对象,例如列表。甚至是字典也能解包,只不过* 解包的字典得到的 是key组成的参数列表 ,和value无关:

    D=dict(a=11,b=22,c=33,d=44)

    f(*D)

    # 输出:

    a

    b

    c

    d

    而 ** 解包的字典则是 key=value 组成的参数列表。以下是函数调用时使用 ** 进行解包,字典D中的key名称必须和def中定义的参数名称相同:

    def f(a,b,c,d):

    print(a)

    print(b)

    print(c)

    print(d)

    D=dict(a=11,b=22,c=33,d=44)

    f(**D)

    # 输出:

    11

    22

    33

    44

    在函数调用时,可以混合位置参数、关键字参数、 * 解包参数、 ** 解包参数。用法非常的灵活:

    def f(a,b,c,d):

    print(a)

    print(b)

    print(c)

    print(d)

    f(*(1,2),**{'d':4,'c':3})

    f(1,*(2,3),**{'d':4})

    f(1,c=3,*(2,),**{'d':4})

    f(1,*(2,3),d=4)

    f(1,*(2,),c=3,**{'d':4})

    上面调用函数时的效果都等同于 f(1,2,3,4) 。

    keyword-only参数形式

    keyword-only的参数传值方式表示def中如果使用了 * ,那么在调用函数时, 它后面的参数必须只能使用关键字传值 。其实在前面的内容中已经出现过几次与之相关的说明。

    另外注意, * 才是keyword-only开关, ** 不是,虽然 ** 也有自己的一些语法限制:任意类型的参数定义都必须在 ** 之前,包括keyword-only类型的参数。这个前面已经解释过了。

    例如:

    def f(a,*b,c):

    print(a,b,c)

    按照keyword-only的规则,被 *b 收集的位置参数不包括c,这个c必须只能使用关键字的方式传值,否则就被当作位置参数被收集到元组b中。

    # 正确

    f(1,2,3,c=4)

    # 错误

    f(1,2,3,4)

    # 错误

    f(1,c=4,2,3)

    其中最后一个错误和如何def的定义无关,而是函数调用时的语法错误,前面已经解释过:位置参数必须放在最前面。

    还可以直接使用 * 而非 *args 的方式,这表示 不收集任何参数,但却要求它后面的参数必须按照关键字传值的方式 。

    def f(a,*,b,c):

    print(a,b,c)

    以下是正确和错误的调用方式示例:

    # 正确

    f(1,b=2,c=3)

    f(1,c=3,b=2)

    f(b=2,c=3,a=1)

    # 错误

    f(1,2,3)

    f(1,2,c=3)

    f(1,b=2,3)

    不过,keyword-only后面的参数可以使用参数默认值。

    def f(a,*,b,c=3)

    那么c是可选的,但如果给定,则必须按关键字方式传值。

    参数定义和参数传值的规则

    对于 函数定义中的参数 ,有3种方式:普通位置参数、 * 开启的keyword-only参数、 **args收集参数。它们之间的规则是:

    **args 必须在最后面

    * 或 *args 后面可以是普通参数,但是函数调用传值时,它后面的参数必须按照关键字的方式指定

    所以,函数定义时参数的通用形式为:其中c和d必须使用关键字传值方式

    def f(a,b, *,c,d, **dicts)

    def f(a,b, *args,c,d, **dicts)

    对于函数调用中的参数,有:普通位置参数、关键字参数、 * 解包参数、 ** 解包参数。它们之间的规则时:

    普通位置参数必须在最前面

    ** 解包必须在最后面

    关键字参数和 * 解包参数只要求在上述两种参数形式中间,顺序可以随意

    所以,函数调用时的传参形式为:

    f(a,b,c, *(d,e,f),g=1,h=2, **dict(j=3,k=4))

    f(a,b,c, d=1,e=2,*(f,g,h), **dict(j=3,k=4))

    例如:

    def f(a,*b,c,**d):

    print(a,b,c,d)

    f(1, 2,3, c=4, x=5,y=6)

    f(1, c=4,*(2,3), **dict(x=5,y=6))

    f(1, *(2,3),c=4, **dict(x=5,y=6))

    f(1, *(2,3), **dict(c=4,x=5,y=6))

    f(1, 2,3, **dict(c=4,x=5,y=6))

    函数注解(annotations)

    python函数有一个名为 __annotations__ 的属性(可以使用dir(Func_Name)来查看)。它表示函数的注解。

    函数的注解使得参数变得更规范、更通用,它有点类似于强调数据类型。但 它们仅仅只是注解,只是给人看,用来起提示作用的,不会对实际的调用有任何影响 。

    例如,下面是没有给注解的函数参数,也就是平时见到的参数方式:

    def myfunc(a,b,c):

    return a+b+c

    myfunc(1,2,3)

    函数的注解分两种:参数注解和返回值注解。

    参数注解:定义在各参数名之后,使用冒号分隔参数和参数的注解

    返回值注解:定义在参数列表之后,冒号之前,使用瘦箭头 -> 分隔

    例如:

    def myfunc(a:'string',b:[1,5],c:int)->int:

    return a+b+c

    print( myfunc(1,2,3) )

    print( myfunc("a","b","c") )

    虽然上面的函数注解提示了参数a是一个字符串,b是一个列表,c是一个int类型的数据,以及返回值是一个int类型的值,但在函数调用的时候,这些"强调"并没有发生影响,只不过在使用该函数的时候,如果使用IDE编写代码,会有代码提示。

    可以通过函数的 __annotations__ 属性查看函数的注解:

    print(myfunc.__annotations__)

    输出:

    {'a': 'string', 'b': [1, 5], 'c': , 'return': }

    可以只对其中一个或多个参数进行注解。

    如果使用了注解,还要设置参数的默认值,则默认值需要在注解的后面。例如:

    def f(a:'string'=4):

    函数注解只对def语句有效,对lambda无效,因为lambda已经限制了函数的定义方式。

    相关文章

      网友评论

        本文标题:python函数基础与python函数的参数细节!

        本文链接:https://www.haomeiwen.com/subject/sgnzxqtx.html