python基础-函数
目录一.定义函数和调用1.1.global关键字1.2.函数的参数可变参数与固定参数1.3.参数混用可变参数、有无默认值参数二.map()函数与lambda表达式2.1 map()将序列中的每个元素进行函数定义的操作2.2 lambda表达式三 变量的作用域问题四. 函数内部对传入的参数的影响一、可变对象修改内容会影响外部1. 列表2. 字典3. 自定义类实例二、不可变对象任何修改都只会创建新对象1. 整数2. 字符串3. 元组三、关键区分修改内容 vs 重新绑定变量一.定义函数和调用定义一个函数的格式def函数名(参数1,参数2,...,参数n):#函数实现return...#函数返回值可以不要实例定义求和函数与调用defcal_sum(n):msum0foriinrange(n1):msumireturnmsum num10print(the sum from 0 to %d %d%(num,cal_sum(num)))输出为the sum from 0 to 10 551.1.global关键字global关键字将函数内的局部变量的作用域变为全局即为全局变量和c/c相比可以在函数内部定义全局变量例1函数外已有与局部变量的同名变量count0defincrement():globalcount count10print(函数内部,id(count),; couunt ,count)increment()print(全局,id(count),; couunt ,count)打印结果通过global修饰局部变量二者的内存相同函数内部 140703368747056 ; couunt 10全局 140703368747056 ; couunt 10若是函数内部无global count声明coun仍为全局变量函数内部可用但是count 10将会创建局部变量count。例2函数外无与局部变量的同名变量defincrement():globalcount count10print(函数内部,id(count),; couunt ,count)increment()print(全局,id(count),; couunt ,count)打印结果内部的局部变量此时实际上已是一个全局变量函数内部 140703368747056 ; couunt 10全局 140703368747056 ; couunt 10例3无globalcount0defincrement():count10print(函数内部,id(count),; couunt ,count)increment()print(全局,id(count),; couunt ,count)打印结果函数内部 140703368747056 ; couunt 10全局 140703368746736 ; couunt 01.2.函数的参数可变参数与固定参数固定参数的默认值defaddTwoNum(n1,n2,n30):print(n1%d,n2%d,n3%d%(n1,n2,n3))returnn1n2n3默认值参数必须放在无默认值参数之后否则会报错。可变参数基于元组的可变参数即位置参数形式为fun(*arg)如下defcalc(*numbers):#可变参数print(numbers)sum0forninnumbers:sumsumn*nreturnsumprint(calc())print(calc(1))print(calc(1,2))print(calc(1,2,3))nums[1,3,5,7,9]print(calc(*nums))#对于一个list或者tuple可直接用这种形式打印结果The tuple is: ()0The tuple is: (1,)1The tuple is: (1, 2)5The tuple is: (1, 2, 3)14The tuple is: (1, 3, 5, 7, 9)165基于字典的可变参数即关键字参数形式为fun(**arg)如下defshow_info(**info):print(The dictionary is,info)forkey,valueininfo.items():#info可看成字典所以可以用字典的方法print({} - {}.format(key,value))show_info(genderM,namexxp)show_info(genderM,namexxp,age18)dir1{name:zhang,age:55}show_info(**dir1))#对于一个字典来说可直接用这种形式打印结果The dictionary is {‘gender’: ‘M’, ‘name’: ‘xxp’}gender - Mname - xxpThe dictionary is {‘gender’: ‘M’, ‘name’: ‘xxp’, ‘age’: 18}gender - Mname - xxpage - 18The dictionary is {‘name’: ‘zhang’, ‘age’: 55}name - zhangage - 55参考1.3.参数混用可变参数、有无默认值参数当函数同时包含无默认值参数、有默认值参数、*args、**kwargs时必须严格按以下顺序定义无默认值参数 → 有默认值参数 → *args → **kwargs1 什么是关键字参数指调用函数时通过 paramvalue 形式传入的参数。deffunc(a,b2):# b 是有默认值的参数print(a,b)# 按位置传递b 此时是位置参数func(1,3)# 输出1 3b 被位置参数 3 覆盖# 按关键字传递b 此时是关键字参数func(1,b4)# 输出1 4b 被关键字参数覆盖# 省略 b使用默认值此时 b 既不是位置也不是关键字参数仅用默认值func(1)# 输出1 22 * 后的参数要传值则必须用关键字形式传递即key ** 的形式deffunc(a,*,b2,c):# * 后b 有默认值c 无默认值print(a,b,c)# 正确* 后的参数必须用关键字传递func(1,b3,c4)# 输出1 3 4func(1,c5)# 输出1 2 5b 用默认值仍需关键字传递或省略# 错误* 后的参数用位置传递func(1,3,4)# 报错b 和 c 被当作位置参数不符合强制关键字要求func(1,b3)# 报错缺少关键字参数 c 3 参数混用参数顺序为普通位置参数无默认值→ 普通参数有默认值→# 有默认值的必须在后面否则报错*args可变位置参数→# 这之后的参数传参必须用关键字形式传参否则报错关键字参数可带可不带默认值强制关键字参数→# 有无默认值的顺序不重要因为是关键字参数**kwargs可变关键字参数# 必须在参数最后面否则报错函数示例deffunc(a,# 1. 普通位置参数无默认值必传b2,# 2. 普通参数有默认值可选*args,# 3. 可变位置参数接收剩余位置参数c3,# 4. 强制关键字参数可带默认值必须用关键字传d,# 5. 强制关键字参数无默认值必须用关键字传**kwargs# 6. 可变关键字参数接收剩余关键字参数):print(fa{a}, b{b}, args{args}, c{c}, d{d}, kwargs{kwargs})func(1,2,3,c3,d4,e5)func(1,2,3,d4,e5)# c使用默认值func(1,2,3,d4,c3,e5)# c d谁先赋值都无所谓func(1,2,3,e5,d4,c3)# e做为可变参数位置在关键字参数里也不重要打印结果a1,b2,args(3,),c3,d4,kwargs{e:5}a1,b2,args(3,),c3,d4,kwargs{e:5}a1,b2,args(3,),c3,d4,kwargs{e:5}a1,b2,args(3,),c3,d4,kwargs{e:5}二.map()函数与lambda表达式2.1 map()将序列中的每个元素进行函数定义的操作举例如下defmulty2(n):returnn*2nums[1,3,5,7,9]iteratormap(multy2,nums)print(iterator)#返回的是物理地址print(list(iterator))#打印结果print(list(iterator))打印结果map object at 0x0000022BA1740F10[2, 6, 10, 14, 18][]不难发现map返回值取完之后就变空了,这一点暂时不明白待后面研究map()参考2.2 lambda表达式也叫lamba函数是一个匿函数名函数基本语法lambda argument_list:expersion可以和函数一样使用fmulti2lambdax:x*2print(fmulti2(4))#打印结果为8print((lambdax:x*2)(4))#打印结果为8前者把lambda表达式赋值给变量此时变量就相当于函数名后者直接在后面接参数就和普通函数一样。结合一些内置函数使用比如上面的map()函数nums[1,3,5,7,9]itemap(lambdan:n*2,nums)foriinite:#map()函数返回值的另一种打印方式print(i)打印结果26101418lambda函数参考三 变量的作用域问题如下main中定义的“nusc”对象可以在此文件中的其他函数中直接使用。是全局作用域原因是在 Python 中if 语句块不会创建新的作用域它使用的是所在位置的当前作用域。在下面的代码里ifname ‘main’: 这一块直接位于模块的顶层没有包在任何函数或类中因此其内部定义的 nusc 实际上是模块的全局变量。defrender_sample_data()-None:...lidiar_render(sample_toekn,pred_data,out_pathout_path)samplenusc.get(sample,sample_toekn)...if__name____main__:...nuscNuScenes(versionv1.0-mini,dataroot./data/nuscenes,verboseTrue)#xxp...隐患如果这个模块.py被其他脚本 import那么 ifname ‘main’: 下的代码不会执行nusc 就根本没有定义。此时若调用 render_sample_data()会抛出 NameError: name ‘nusc’ is not defined。四. 函数内部对传入的参数的影响函数内部的实现对传入变量是否有影响关键在于传入的是可变对象 vs 不可变对象。一、可变对象修改内容会影响外部1. 列表defmodify_list(lst):lst.append(4)# 修改列表内部的内容lst[0]100# 同样是修改内容a[1,2,3]modify_list(a)print(a)# [100, 2, 3, 4] ← 外部被改变解释lst和外部a指向同一个列表对象append和索引赋值都是修改这个对象本身。2. 字典defmodify_dict(d):d[new_key]new_value# 修改字典内容info{name:Alice}modify_dict(info)print(info)# {name: Alice, new_key: new_value}同样字典也是可变对象函数内添加键值对直接反映到外部。3. 自定义类实例classNode:def__init__(self,val):self.valval self.nextNonedefchange_node(n):n.val999# 修改对象属性n.nextlinked# 修改对象属性nodeNode(10)change_node(node)print(node.val,node.next)# 999 linked二、不可变对象任何修改都只会创建新对象1. 整数defadd_one(x):xx1# 创建新的整数对象重新绑定局部变量 xa10add_one(a)print(a)# 10不变x x 1并不是修改原来的10而是计算出一个新整数11然后让局部变量x指向它。外部a依然指向10。2. 字符串defadd_str(s):ss world# 同样生成新字符串并重新绑定greetinghelloadd_str(greeting)print(greeting)# hello不变字符串是不可变的任何拼接操作都会产生新字符串。3. 元组defmodify_tuple(t):# t[0] 999 # 会直接报错因为元组不可变t(4,5,6)# 只是重新绑定局部变量tup(1,2,3)modify_tuple(tup)print(tup)# (1, 2, 3)元组本身不能修改内容t (4,5,6)只是让局部变量指向新元组不影响外部。三、关键区分修改内容 vs 重新绑定变量同一个可变对象也有“重新绑定”的问题defreassign_list(lst):lst[7,8,9]# 重新绑定不是修改原列表lst.append(10)a[1,2,3]reassign_list(a)print(a)# [1, 2, 3] ← 完全没有被改变解释lst [7,8,9]把局部变量lst指向了一个全新的列表对象从此与外部a没有任何关系。后面的append也是在新列表上操作。只有通过lst.append()、lst[0] ...这类不改变变量绑定的操作才能影响外部传入的同一个对象。一句话总结可变对象 修改内容→ 外部看得见变化。不可变对象 / 可变对象 重新绑定变量→ 外部看不见变化。