首页 > 其他分享 >函数进阶 - 2

函数进阶 - 2

时间:2023-03-30 21:02:53浏览次数:32  
标签:进阶 函数 args test kwargs print name

8. 函数进阶 - 2

函数参数的高级用法

缺省参数
引入

缺省参数也叫做默认参数,是指定义函数时形参变量有默认值,如果调用函数时没有传递参数,那么函数就用默认值,如果传递了参数就用传递的那个数据。

示例:

def print_info(name, age=35):
    print(f'name: {name}')
    print(f'age: {age}')

print_info('顾安')
print_info('顾安', 18)
缺省参数的作用

当调用函数时,有些参数不必传递,而是用默认值,这样的场景往往都用缺省参数

例如,一个学校现在开始检查每个学生的信息,学生说:报告老师我是xxx学校xxx系xxx年级xxx班学生,名字叫xxxx,大家想只要是这学校的学生那么“xxx学校”就可以省略不用说了,因为大家都知道。所以就可以认为默认的学校就是xxx,而当其他学校的学生介绍时yyy学校名字案例说就一定要说清楚,否则让人弄混了。

示例:

def print_info(name, class_name, grade, department_name, school_name="图灵学院"):
    print("老师好:我是来自 %s(大学) %s系 %s年级 %s班级 的学生,我叫%s" % (
        school_name,
        department_name,
        grade,
        class_name,
        name
    ))


print_info("顾安", "爬虫", "二", "软件工程")
print_info("顾安", "爬虫", "二", "软件工程", "图灵python")
注意点
  • 缺省参数只能在形参的最后(即最后侧)
  • 缺省参数全挨在一起(在右侧),不是缺省参数挨在一起(在左侧)
>>> def printinfo(name, age=35, sex):
...     print name
...
File "<stdin>", line 1
SyntaxError: non-default argument follows default argument
命名参数
引入

命名参数是指:在调用函数时,传递的实参带有名字,这样的参数叫做命名参数

示例:

def test(a, b):
    print('-----')
    print(f'a={a}')
    print(f'b={b}')

test(11, 22)
test(a=11, b=22)
test(a=22, b=11)  # 根据名称将值传入到指定的变量中
命名参数的作用

命名参数能够在调用函数的时候,不受位置的影响,可以给需要的参数指定传递数据

注意点
  • 命名参数的名字要与形参中的名字相同,不能出现命名参数名字叫做num,而形参中没有变量num
  • 如果形参左侧有普通的形参,调用函数时传递的参数一定要先满足这些形参,然后再根据需要编写命名参数
def test(a, b, c=100, d=200):
    print("a=%d, b=%d, c=%d, d=%d" % (a, b, c, d))


# 下面的方式都成功
test(11, 22)
test(11, 22, 33)
test(11, 22, 33, 44)
test(11, 22, d=33, c=44)

# 下面的方式都失败
test(c=1, d=2)  # 缺少a、b的值
test(c=1, d=2, 11, 22)  # 11, 22应该在左侧
不定长参数
引入

不定长参数:定义函数的时候形参可以不确定到底多少个,这样的参数就叫做不定长参数

不定长参数有2种方式表示

  • *args :表示调用函数时多余的未命名参数都会以元组的方式存储到args
  • **kwargs:表示调用函数时多余的命名参数都会以键值对的方式存储到kwargs

注意:

  • ***是必须要写的,否则就变成了普通的形参了
  • 当我们说不定长参数的时候,就是指*args**kwargs

示例:

def test(a, b, *args, **kwargs):
    print(a, type(a))
    print(b, type(b))
    print(args, type(args))
    print(kwargs, type(kwargs))

test(11, 22, 33, 44, 55, 66, name='顾安', address='长沙')
不定长参数的作用

通过不定长参数,能够实现调用函数时传递实参个数可以随意变换的需求

注意点
  • 加了星号*的变量args会存放所有未命名的变量参数,args为元组
  • 而加**的变量kwargs会存放命名参数,即形如key=value的参数, kwargs为字典
  • 一般情况下*args**kwargs会在形参的最右侧
  • argskwargs的名字可以变,例如叫*aa**bb都是可以,但一般为了能够让其他的开发者快速读懂我们的代码最好还是不改
特殊情况

缺省参数在*args的后面

def sum_nums_3(a, *args, b=22, c=33, **kwargs):
    print(a)
    print(b)
    print(c)
    print(args)
    print(kwargs)


sum_nums_3(100, 200, 300, 400, 500, 600, 700, b=1, c=2, mm=800, nn=900)

说明:

  • *args后可以有缺省参数,想要给这些缺省参数在调用时传递参数,需要用命名参数传递,否则多余的未命名参数都会给args
  • 如果有**kwargs的话,**kwargs必须是最后的

输出结果:

100
1
2
(200, 300, 400, 500, 600, 700)
{'mm': 800, 'nn': 900}

函数返回值拆包

什么是函数返回值拆包

函数返回值拆包:如果一个函数通过return返回了一个元组、列表、集合,可以通过拆包的方式将返回值进行拆分到每个变量中,这就是返回值拆包。

示例:

def test():
    return 11, 22, 33

a, b, c = test()
print(a, b, c)
返回值拆包的作用

通过函数返回值拆包,可以快速的将具体的数据用变量进行存储,这样对数据的处理会更加方便

示例:

def test():
    return 11, 22, 33


# 通过返回值拆包,快速使用每个数据
a, b, c = test()
print(a + b + c)

# 没有通过返回值拆包,这样用数据时稍微复杂
ret = test()
print(ret[0] + ret[1] + ret[2])
拆包的使用
def get_my_info():
    high = 178
    weight = 100
    age = 18
    return high, weight, age


# result = get_my_info()
# print(result)

# 通过返回值拆包,能够更加方便的对每个数据使用
my_high, my_weight, my_age = get_my_info()
print(my_high)
print(my_weight)
print(my_age)
使用拆包时的注意点
  • 拆包时要注意,需要拆的数据的个数要与变量的个数相同,否则程序会异常

通过星号拆包

通过普通方式拆包

假如有以下函数:

def test(a, b, c):
    print(a + b + c)

现在自己拥有的数据:

nums = [11, 22, 33]

怎样才能在调用test函数的时候,将nums给传递过去呢?

def test(a, b, c):
    print(a + b + c)


nums = [11, 22, 33]
test(nums[0], nums[1], nums[2])

上述代码用的方式虽然能行,但不是很简洁

为了能够用更加简洁的方式实现上述场景需求,Python可以通过***将数据拆包后传递

使用*拆包

有时在调用函数时,这个函数需要的是多个参数,而自己拥有的是一个列表或者集合这样的数据,此时就用可以用*拆包

使用方式:

*列表
*元组
*集合

*拆包的方式实现上述功能:

def test(a, b, c):
    print(a + b + c)


nums = [11, 22, 33]
test(*nums)  # 此时的*的作用就是拆包,此时*nums相当于11, 22, 33 即test(11, 22, 33)

如果为数据元组时使用方式与上述代码一致:

def test(a, b, c):
    print(a + b + c)


nums = (11, 22, 33)
test(*nums)

集合类型同上:

def test(a, b, c):
    print(a + b + c)


nums = {11, 22, 33}
test(*nums)

注意:

  • *对列表、元组、集合可以拆包,但一般都是在调用函数时用
使用**拆包

使用**可以对字典进行拆包,拆包的结果是命名参数

示例:

def test(name, age, address):
    print(name)
    print(age)
    print(address)


info = {
    "name": "顾安",
    "age": 18,
    "address": "长沙"
}

test(**info)
'''
当前**info相当于以下代码:
	name='顾安'
	age=18
	address='长沙'
	
** 主要对字典进行拆包
'''
难点

学习不定长参数时,掌握了*args**kwargs

现在学习拆包时,也用到了***

那它们之间有什么关系呢?

答:没有任何关系,只是长得像罢了

示例一:

def test1(*args, **kwargs):
    print("----在test1函数中----")
    print("args:", args)
    print("kwargs", kwargs)


def test2(*args, **kwargs):
    print("----在test2函数中----")
    print("args:", args)
    print("kwargs", kwargs)
    test1(args, kwargs)  # 在函数test1传递参数时没有进行拆包


test2(11, 22, 33, name="顾安", age=18)

运行结果:

----在test2函数中----
args: (11, 22, 33)
kwargs {'name': '顾安', 'age': 18}
----在test1函数中----
args: ((11, 22, 33), {'name': '顾安', 'age': 18})
kwargs {}

示例二:

def test1(*args, **kwargs):
    print("----在test1函数中----")
    print("args:", args)
    print("kwargs", kwargs)


def test2(*args, **kwargs):
    print("----在test2函数中----")
    print("args:", args)
    print("kwargs", kwargs)
    test1(*args, **kwargs)  # 对参数进行了拆包


test2(11, 22, 33, name="顾安", age=18)

运行结果:

----在test2函数中----
args: (11, 22, 33)
kwargs {'name': '顾安', 'age': 18}
----在test1函数中----
args: (11, 22, 33)
kwargs {'name': '顾安', 'age': 18}

Python语言中的引用

引入

如下代码中,最后b的值为多少?

>>> a = 1
>>> b = a
>>> b
1
>>> a = 2
>>> a
2

如下代码中,最后b的值为多少?

>>> a = [1, 2]
>>> b = a
>>> b
[1, 2]
>>> a.append(3)
>>> a
[1, 2, 3]
什么是引用

引用:就是地址

那地址是什么呢?可以理解为存放数据的空间在内存中的编号

例如:

a = 100

怎样知道它的地址呢?

id(a)

可以直接将上述的结果打印:

print(id(a))

运行结果(在不同机器上输出的地址可能不相同):

4347271232

当我们知道了原来引用就是地址之后,再来看如下代码:

a = [1, 2]

我们可以用id(a)取它的地址:

a = [1, 2]
print(id(a))  # 获取变量存储的引用(地址)是多少

接下来定义变量b并且赋值:

a = [1, 2]
print(id(a))  # 获取变量存储的引用(地址)是多少

b = a

此时输出变量b的引用:

a = [1, 2]
print(id(a))

b = a
print(id(b))

运行结果(不同机器上的内存地址可能不相同):

4558971360
4558971360

这说明,此时变量a、b存储的引用都是相同的

由此我们可以得出一个结论:Python中的变量并不是真正存储数据,而是存储的数据所在内存中的地址,我们一般称之为引用

既然变量a、b都指向同一个列表,那么接下来

a.append(3)

此时变量a、b指向的同一个列表中多了一个数据,即此时列表为[1, 2, 3]

所以a、b此时用print输出相同的结果

补充内容

大家自己试试看a=257, b=257时它们的id还是否会相等。事实上Python 为了优化速度,使用了小整数对象池,避免为整数频繁申请和销毁内存空间。而Python 对小整数的定义是 [-5, 257),只有数字在-5到256之间它们的id才会相等,超过了这个范围就不行了,同样的道理,字符串对象也有一个类似的缓冲池,超过区间范围内自然不会相等了。**

总的来说,只有数值型和字符串型,并且在通用对象池中的情况下,a is b才为True,否则当a和b是int,str,tuple,list,dict或set型时,a is b均为False。

赋值运算符=

赋值运算符=,之前为了更好的理解变量,把a=100理解为变量a中存放了100,事实上变量a存储是100的引用

也就是说:在Python中只要用=那么就表示=左边的变量存储了一个新的引用

大白话讲:就是=左边的变量指向了右边的数据

想想下面的代码运行的结果是什么?

a = [1, 2]
b = a
b.append(3)
b = [100, 200, 300]

print(b)

运行结果:

[100, 200, 300]

而不是:

[1, 2, 3]
引用当做实参

Python中调用函数时,传递实参实际上都是是引用,即传递的都是地址

只要是传递的引用,那么也就是说在函数中是可以直接对指向的数据进行修改

def test(p):
    # 此时变量p也指向nums指向的列表
    p.append(44)
    print("在函数test中,p=", p)


nums = [11, 22, 33]
print("调用test函数之前,nums=", nums)
test(nums)  # 此时将列表的引用当做了实参进行传递
print("调用test函数之后,nums=", nums)

运行结果:

调用test函数之前,nums= [11, 22, 33]
在函数test中,p= [11, 22, 33, 44]
调用test函数之后,nums= [11, 22, 33, 44]

函数名也是引用

引入

阅读如下代码,思考会输出什么结果

def test1():
    print("我是test1函数哦。。。。")


def test2():
    print("我是test2函数哦。。。。")


test1()

test1 = test2
test1()

运行结果如下:

我是test1函数哦。。。。
我是test2函数哦。。。。

你可能会惊讶,为什么第9行调用test1函数输出的是我是test1函数哦。。。。,反而到了第12行再次调用test1函数时变成了我是test2函数哦。。。。

上述问题的原因核心点是:在Python中即使是函数名也是一个变量名,只不过这个变量没有指向普通的数据,而是指向了一段代码;也就是说如果定义了一个函数名字叫做test1就好比是一个变量名test1指向了那个代码块而已,所以当上述代码第11行test1 = test2时,就相当于让test1变量不在指向原本的代码块,而是指向新的代码块即test2指向的代码块,所以当第12行执行test1函数时,会输出我是test2函数哦。。。。

引用的作用

看完上述的引入知识后,相信你会对什么是函数的引入有一个大体的认知了

在此简单总结:所谓函数名当做引用,其实是指在Python中所有的函数名实际上是一个变量名,只不过这个变量名指向的不是常见的数据,而是一段代码,当我们用函数名()是实际上就是让指向的这块代码开始执行,当我们只用函数名时其实就是这个函数的引用

记住:既然函数名也是变量名,那么就可以给它赋值获取它的引用给别的变量

总结:

  1. 使用def定义的函数名,实际就是个变量名它存储了函数的引用
  2. 如果将另外一个变量,例如b保存了函数的引用,即也指向了同一个函数,那么b()就是调用函数

匿名函数

什么是匿名函数

没有名字的函数,在Python中用lambda定义

示例:

lambda x, y: x + y  # 定义了一个匿名函数 1.没有名字 2.完成2个数的加法操作
匿名函数的作用
  1. 可以用一行代码完成简单的函数定义
  2. 可以当做实参快速传递到函数中去
使用方式

lambda关键词能创建匿名函数。这种函数得名于省略了用def声明函数的标准步骤

lambda函数的语法只包含一个语句,如下:

lambda 形参1, 形参2, 形参3: 表达式

注意点:lambda函数能接收任何数量的参数但只能返回一个表达式的值,其默认就是返回的,不用写return

既然我们已经知道def定义函数时的变量存储的是函数的引用,所以只要有了这个函数的引用,也就可以通过变量名()的方式调用函数

而函数分为def定义的普通函数,和用lambda定义的匿名函数,所以无论一个变量例如b保存的是普通函数的引用,还是匿名函数的引用,都可以用b()方式调用b指向的函数

一般情况下对匿名函数的使用有2种方式

  1. 通过lambda定义匿名函数,然后用一个变量指向这个匿名函数,然后通过变量名()调用这个匿名函数
  2. 直接在调用其它函数实参的位置通过lambda定义匿名函数,会将这个匿名函数的引用当做实参进行传递

方式一:

# 定义了一个匿名函数,然后让变量add_2_nums指向它
add_2_nums = lambda x, y: x + y

# 调用add_2_nums指向的匿名函数
print("10+20=" % add_2_nums(10, 20))

输出结果:

10+20=30

方式二:

def fun(a, b, opt):
    print("a = %d" % a)
    print("b = %d" % b)
    print("result = %d" % opt(a, b))  # 此时opt指向了第7行定义的匿名函数,所以opt(a, b)就相当于调用匿名函数


fun(1, 2, lambda x, y: x + y)  # 定义一个匿名函数,且将它的引用当做实参进行传递
代码案例

想一想,下面的数据如何指定按agename排序?

stus = [
    {"name": "顾安", "age": 18},
    {"name": "夏洛", "age": 19},
    {"name": "木木", "age": 17}
]

按照name排序:

stus = [
    {"name": "顾安", "age": 18},
    {"name": "夏洛", "age": 19},
    {"name": "木木", "age": 17}
]

print("排序前,stus=", stus)
stus.sort(key=lambda x: x['name'])
print("排序后,stus=", stus)

按照age排序:

stus = [
    {"name": "顾安", "age": 18},
    {"name": "夏洛", "age": 19},
    {"name": "木木", "age": 17}
]

print("排序前,stus=", stus)
stus.sort(key=lambda x: x['age'])
print("排序后,stus=", stus)

标签:进阶,函数,args,test,kwargs,print,name
From: https://www.cnblogs.com/wt-poppies/p/17274274.html

相关文章

  • HCIP-ICT实战进阶10-BFD原理与配置
    HCIP-ICT实战进阶10-BFD原理与配置0引言之前学习的比如链路聚合、STP、RSTP、MSTP以及一些路由协议,所有的协议都可以实现一种能力:冗余备份网络中如果真的发生了设备的故障或者是链路故障,则以上的各种协议需要检测到故障,然后进行网络的链路切换.生成树协议发生故障生成树......
  • hdu3980 Paint Chain SG函数+SG定理+记忆化搜索
    liyishui今天学习博弈,因为liyishui今天写树链剖分写得有点理智--题意:有一个圆,上面有n个豆子,每次要挑出连续m个没染色的豆子进行染色,不能移动时输掉游戏问先手必胜还是后手必胜,其中n、m<=1000题解:会很朴素地想到如果第一个人拿走了m个,那么剩下的就是一条链的问题。所以可以先......
  • [Python]异步回调函数
    importasynciofromfunctoolsimportpartialfromasyncioimportFutureasyncdeff1():print(1)awaitasyncio.sleep(2)print(2)return"f1"defcallback1(future:Future):print(future.result())print("我是f1的回调函数&......
  • 跟着查老四学Python Day 3:数据结构与函数
    老猫:请您扮演一个python讲师,帮助一个没有代码经验的人自学python,以下是此前你设置的学习计划制定学习计划:将学习过程分为四个阶段,每个阶段关注不同的内容。第一周:掌握Python基础语法、数据类型、控制结构等。同时,学会如何在本地搭建Python开发环境。第二周:学习函数、模块、文件操......
  • Java 8 函数式编程
    1Java8函数式编程2java.util.function.*3@FunctionalInterface4都是函数接口,没有成员(状态)56高阶函数:参数或返回值为函数78方法引用:类名::方法......
  • 【Python】函数的可变参数 *args 和 **kwargs的使用
    可变参数*args和**kwargs*args和**kwargs主要用于定义函数的可变参数,*args和**kwargs组合起来可以传入任意的参数。(注意:参数*args必须在**kwargs之前定义)*arg......
  • 转:np.zeros()函数
    函数调用方法:numpy.zeros(shape,dtype=float)各个参数意义:shape:创建的新数组的形状(维度)。dtype:创建新数组的数据类型。返回值:给定维度的全零数组。基础用法:import......
  • Linux系统下exec函数族简单介绍
    exec()函数的简单介绍exec函数族的作用是根据指定的文件名找到可执行的文件,并用它来取代调用进程的内容,话句话说,就是在调用进程内部执行一个可执行文件。exec函数族的函......
  • DM78经常使用的命令汇总3-函数
    1.函数类---子分区编号selectSF_GET_HP_SEQ_NO(1216);selectSF_GET_HP_SEQ_NO('SYSDBA','TEST_P01');--查看子分区相对编号selectSF_GET_PART_SEQNO(1215,......
  • SDL_PauseAudio 声音播放/暂停函数剖析
    函数说明用此函数来暂停播放音频,或播放音频。根据参数来决定,0是播放音频,非0是暂停播放音频切记,并不是停止播放音频!函数声明/***\namePauseaudiofunctions**T......