函数 part1

一、函数的基本概念

'''原则:先定义后调用'''
1.函数:盛放功能的容器,让代码更加条理清晰,方便使用
2.定义函数
	def func() :    
    	"""        # type hinting 类型提示
    
    	:param x:  # 参数的类型
    	:param y:  
    	:return:   # 返回值类型
    	"""
        函数体,即代码块
    # 提示信息书写方式
    	# def func(x:int ,y:'传入字符串') -> '返回值是整型'
    	# int、'传入字符串'、'返回值是整型',非硬性限制只用来提示
    # 推荐:
    def func(x:int ,y:str) -> int	
3.调用
    # 两步
    foo()  
        foo:函数的内存地址
        ():运行函数体
    print(foo)
        结果为内存地址<function foo at 0x0000023046EA5F70>
# 例子
    def foo(x,y):
        print(x,y)
    res = foo(1, 2)
    print(res)  # 结果为None,因为函数没有返回值
    res() # res不是函数,未绑定内存地址
4.定义函数的三种格式
    # 无参函数
    def foo():
        pass
    # 有参函数
    def bar(x,y):
        pass
    # 空函数:无函数体代码块,用来占位的
    def spam():
        pass
        ...
5.调用函数的三种形式
    def foo():
        return 666
    res = foo()*10   # 表达式,加入运算符
    foo()            # 直接调用,执行命令
    print(foo())     # 当作参数传给另外的函数,print()也是函数
6.函数的返回值
    def foo(): 
        return 123,'x',[1,2,3]  # 返回值是一个元组
    a,b,c = foo()  # 解压赋值
    print(a,b,c)   
    # return 可以没有,没有或者return 或 return None,返回值都为None
    # 函数内可以有多个return,只要执行一次,整个函数结束,将后面的值当作本次调用的值返回 
    # 好比break,可以结束函数

二、函数的参数

# 基本概念
形参:函数定义阶段,括号内指定的参数(变量名)
实参:函数调用阶段,括号内传入的值(变量值)
两者的关系:在函数调用时,会将实参值绑定给形参名;函数调用完毕后则解除绑定

1.位置形参
	定义:在函数定义阶段,按照从左到右的顺序依次定义的形参
	特点:必须被传值,多一个不行,少一个不行
	def foo(x, y, z):
        print(x, y, z)
    foo(3, 2, 1)  # 3 2 1  xyz一一对应
   	foo(3, 2, 1, 0)  # 报错,多了不行
    foo(3, 2)  # 报错,少了不行
2.位置实参
	定义:在函数调用阶段,按照从左到右的顺序依次传入的值
	特点:按照顺序依次为形参赋值,遵循形参的规则
	def foo(x, y, z):
        print(x, y, z)
	foo(1, 2, 3)  # 1 2 3 
	foo(3, 2, 1)  # 3 2 1
3.关键字实参
	定义:在函数调用阶段,按照key=value的格式来传值
	特点:可以打乱顺序,但仍能为指定的形参赋值
	def foo(x, y, z):
        print(x, y, z)
	foo(x=1, y=2, z=3)  # 1 2 3
	foo(y=2, x=1, z=3)  # 1 2 3
# 注意:位置实参与关键字实参可以混用,但是遵循两点:
	1)位置实参必须跟在关键字实参前面
	2)不能为同一个形参重复赋值
	def foo(x, y, z):
        print(x, y, z)
	foo(1, y=2, z=3)
	foo(y=2, 1, z=3)  # 报错 1)positional argument follows keyword argument
	foo(1, 2, y=4)  # 报错 2)foo() got multiple values for argument 'y'
4.默认形参
	定义:在函数定义阶段,就已经为形参赋值
	特点:定义阶段就已经有值了,意味着在调用阶段可以不用为其传值,若为其传值则以传的值为主
	应用:输出大部分参数的值相同时,可以用到默认参数,例如统计性别
	def foo(x, y=222):
        print(x, y)
	foo(111,)  # 111 222
	foo(111, 333)  # 111 333
    # 例子
        def register(name, age, gender='male')
            print(name, age, gender)
        register("richer", 20, )  #  richer 20 male
        register("tom", 19, )
        register("jack", 39, )
        register("lili", 19, 'female')  # lili 19 female
# 注意:位置形参和默认形参可以混用,但要遵循三点:
	1)位置形参必须跟在默认形参的前面
	2)默认形参的值推荐指向不可变类型,避免后续代码影响函数
	3)默认形参的值只在定义阶段被赋值一次,不是每次调用函数都执行默认的参数代码,但函数体代码每次调都会执行
	# 例1
		m = 100  # m -> 100 的内存地址
		def foo(x, y=m):  # y -> 100 的内存地址
			print(x, y)
		m = 200  # m -> 200 的内存地址,解除100的绑定
		foo(1)  # 1 100    m不影响y,100是不可变类型
	# 例2
		m = [11, 22, 33]  # m -> [  ] 的内存地址
		def foo(x, y=m):  # y -> [  ] 的内存地址
			print(x, y)
		m.append(44)  # m -> [  ] 的内存地址,内存地址不变
		foo(1)  # 1 [11, 22, 33, 44]    m影响y,[  ]是可变类型
	# 例3
		def foo(name, hobby, hobbies=[]): # 默认形参的值只在定义阶段被赋值一次
			hobbies.append(hobby)
			# print(f"{name}喜欢的语言:{hobbies}")
			print("%s的爱好%s" % (name, hobbies))	
		foo('lili', 'read')  # lili的爱好['read']
		# hobbies=[],赋值的是可变类型,第二次调用,相当于只运行hobbies.append(hobby)
		foo('caca', 'play')  # caca的爱好['read', 'play']
		foo('mimi', 'run')   # mimi的爱好['read', 'play', 'run']
	# 例3问题解决方案:大前提让hobbies用之前都变为空列表;让hobbies变为不可变类型
		# def foo(name, hobby, hobbies=666): # hobbies变为不可变类型
		def foo(name, hobby, hobbies=None):  # 优化         
			# if hobbies == 666  # hobbies用之前都变为空列表
			if hobbies is None   # 优化  
				hobbies = []  # 没有给hobbies值,则默认None的时候,清空列表
			hobbies.append(hobby)
			# print(f"{name}喜欢的语言:{hobbies}")
			print("%s的爱好%s" % (name, hobbies))	
		foo('lili', 'read')  # lili的爱好['read']     没有给hobbies值
		foo('caca', 'play')  # caca的爱好['play']
		foo('mimi', 'run')   # mimi的爱好['run']
5.可变长参数:* 与 **
   1)在形参中带*:* 会将溢出的位置实参值汇总成元组,然后赋值给紧跟其后的形参名
	def foo(x,*y):  # y = (2,3,4,5,6)  位置形参在前
		print(x, y)
	foo(1)  # 1 ()
	foo(1,2,3,4,5,6)  # 1 (2, 3, 4, 5, 6)
   2)在形参中带**:** 会将溢出的关键字实参值汇总成字典,然后赋值给紧随其后的形参名
	def foo(x,**y):  # y = {'a':11, 'b':22, 'c':33}  位置形参在前
		print(x, y)
	foo(1)  # 1 {}
	foo(1, a=11, b=22, c=33)  # 1 {'a':11, 'b':22, 'c':33}
   3)在实参中带*:* 会把实参中的可被for循环遍历的数据类型打散,分别对应传值给形参名
	def foo(x, y, z): 
		print(x, y, z)
	foo(*[11, 22, 33])  # 11 22 33  相当于foo(11, 22, 33)
	foo(*'hello')  # 报错  相当于foo("h","e","l","l","o") 打散后传值遵循形参规则,多了不行,少了不行
   4)在实参中带**:** 会把实参中的字典打散,分别对应传值给形参名
	def foo(x, y, y):  # y = {'a':11, 'b':22, 'c':33}  位置形参在前
		print(x, y, y)
	foo(*{'a':11, 'b':22, 'c':33})  # a b c 相当于 foo('a', 'b', 'c') 
	foo(**{'a':11, 'b':22, 'c':33})  # 报错 相当于 foo(c=3, a=1, b=2) 字典无序
	foo(**{'x':11, 'y':22, 'z':33})  # 11 22 33 相当于 foo(y=2, z=3, x=1)
   5)* 与 ** 混合使用
	作用:a.函数可以接收任何形式、任意长度的参数
          b.函数内部可以把接收到的参数传递给另一个函数
	# 例子
     def index(x, y, z):
		print(x, y, z)
     def bar(*args, **kwargs)  # args = (1, 2, 3), kwargs = {'x':11, 'y':22, 'z'=33}
		index(*args, **kwargs)  # index(*(1, 2, 3), **{'x':11, 'y':22, 'z'=33})
							  # index(1, 2, 3, x=11, y=22, z=33)	
     bar(1, 2, 3, x=11, y=22, z=33)  # 报错  要遵循形参规则
     bar(1, c=3,b=2)  # 1 2 3  相当于index(1, c=3, b=2)
   6)命名关键字参数  # 了解
     位置形参、默认形参、*、**的组合,位置参数在前
	 def foo(x,y,*args,m=666,n):
		 print(x,y,m,n)
		 print()

	 foo(1,2,m=777,n=888)
	 foo(1,2,n=999)

	 def foo(x,y=111,*args,m,n,**kwargs):
		 print(x,y,args,m,n,kwargs)
	 foo(1,2,3,4,m=777,n=888)
     实参传值形式为key = value,其他形式多余的会被args,keargs打包带走      
posted @ 2021-06-30 14:34  Richer-J  阅读(87)  评论(0)    收藏  举报