# 认识函数 ## 什么是函数 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。 ## 函数的定义与调用 ```python def my_len(): s = 'hello world' length = 0 for i in s: length = length + 1 print(length) my_len() ``` ``` 定义:def 关键词开头,空格之后接函数名称和圆括号(),最后还有一个":"。    def 是固定的,不能变,他就是定义函数的关键字。    空格 为了将def关键字和函数名分开,必须空(四声),当然你可以空2格、3格或者你想空多少都行,但正常人还是空1格。    函数名:函数名只能包含字符串、下划线和数字且不能以数字开头。虽然函数名可以随便起,但我们给函数起名字还是要尽量简短,并能表达函数功能    括号:是必须加的,先别问为啥要有括号,总之加上括号就对了! 注释:每一个函数都应该对功能和参数进行相应的说明,应该写在函数下面第一行。以增强代码的可读性。 调用:就是 函数名() 要记得加上括号。 ``` ## 函数的返回值 ```python def my_len(): s = 'hello world' length = 0 for i in s: length = length + 1 return length str_len = my_len() print(str_len) ``` **return关键字的作用** - return 是一个关键字,这个词翻译过来就是“返回”,所以我们管写在return后面的值叫“返回值”。 - 不写return的情况下,会默认返回一个None - 一旦遇到return,结束整个函数。 - 返回的多个值会被组织成元组被返回,也可以用多个值来接收 ```python def ret_demo(): return 1,2,'a',['hello','world'] ret = ret_demo() print(ret) ``` ## 函数的参数 带参数的函数 ```python def my_len(s): length = 0 for i in s: length += 1 return length ret = my_len('hello world!') print(ret) ``` 实际的要交给函数的内容,简称实参。 在定义函数的时候它只是一个形式,表示这里有一个参数,简称形参。 1. 按照位置传值:位置参数 ```python def maxnumber(x,y): the_max = x if x > y else y return the_max ret = maxnumber(10,20) print(ret) ``` 1. 按照关键字传值:关键字参数。 ```python def maxnumber(x,y): the_max = x if x > y else y return the_max ret = maxnumber(y = 10,x = 20) print(ret) ``` 1. 位置、关键字形式混着用:混合传参。 ```python def maxnumber(x,y): the_max = x if x > y else y return the_max ret = maxnumber(10,y = 20) print(ret) ``` 位置参数必须在关键字参数的前面 对于一个形参只能赋值一次 1. 默认参数。 ```python def stu_info(name,age = 18): print(name,age) stu_info('aaron') stu_info('song',50) ``` 1. 默认参数是一个可变数据类型 ```python def demo(a,l = []): l.append(a) print(l) demo('abc') demo('123') ``` 1. 动态参数 ```python def demo(*args,**kwargs): print(args,type(args)) print(kwargs,type(kwargs)) demo('aaron',1,3,[1,3,2,2],{'a':123,'b':321},country='china',b=1) ##动态参数,也叫不定长传参,就是你需要传给函数的参数很多,不定个数,那这种情况下,你就用*args,**kwargs接收,args是元祖形式,接收除去键值对以外的所有参数,kwargs接收的只是键值对的参数,并保存在字典中。 ``` ## 命名空间和作用域 代码在运行伊始,创建的存储“变量名与值的关系”的空间叫做全局命名空间; 在函数的运行中开辟的临时的空间叫做局部命名空间。 命名空间一共分为三种: - 全局命名空间 - 局部命名空间 - 内置命名空间 取值顺序: - 在局部调用:局部命名空间->全局命名空间->内置命名空间 - 在全局调用:全局命名空间->内置命名空间 作用域 - 全局作用域:包含内置名称空间、全局名称空间,在整个文件的任意位置都能被引用、全局有效 - 局部作用域:局部名称空间,只能在局部范围内生效 ### globals和locals方法 ```python print(globals()) print(locals()) def func(): a = 12 b = 20 print(globals()) print(locals()) func() ``` **global** 1. 声明一个全局变量。 2. 在局部作用域想要对全局作用域的全局变量进行修改时,需要用到 global(限于字符串,数字)。 ```python def func(): global a a = 3 func() print(a) count = 1 def search(): global count count = 2 search() print(count) ``` 对可变数据类型(list,dict,set)可以直接引用不用通过global ```python li = [1,2,3] dic = {'name':'aaron'} def change(): li.append(4) dic['age'] = 18 print(dic) print(li) change() print(dic) print(li) ``` **nonlocal** 1. 不能修改全局变量。 2. 在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且引用的哪层,从那层及以下此变量全部发生改变。 ```python def add_b(): b = 1 def do_global(): b = 10 print(b) def dd_nolocal(): nonlocal b # 应用了上一层的变量b b = b + 20 print(b) # 发生了改变 dd_nolocal() # 调用函数,导致do_global的命名空间b也改变了 print(b) do_global() print(b) add_b() # 最上面一层没有变化 ``` ## 函数的嵌套和作用域链 ```python def mymax(x,y): m = x if x > y else y return m def maxx(a,b,c,d): res1 = mymax(a,b) res2 = mymax(res1,c) res3 = mymax(res2,d) return res3 ret = maxx(23,453,12,-13) print(ret) # 传入任意多的数字,甚至有重复的,还有非数字(忽略),得到最大的值 def my_max(x, y): m = x if x > y else y return m def maxx(*nums): temp_nums = [] for i in nums: if type(i) is int: temp_nums.append(i) while len(temp_nums) > 1: a_num = temp_nums.pop() b_num = temp_nums.pop() ret = my_max(a_num, b_num) temp_nums.append(ret) return temp_nums[0] ret = maxx(123, 3345, 123, 9999, 123, 345, 345, 667, 123, 435, 657, 16, 21, "aabc", ["a", "b"]) print(f"数值类型中最大的是{ret}") ``` ```python def f1(): print("in f1") def f2(): print("in f2") f2() f1() ``` ## 函数名的本质 函数名本质上就是函数的内存地址 1. 可以被引用 ```python def func(): print('in func') f = func print(f) f() ``` 1. 可以被当作容器类型的元素 ```python def f1(): print('f1') def f2(): print('f2') def f3(): print('f3') l = [f1,f2,f3] d = {'f1':f1,'f2':f2,'f3':f3} #调用 l[0]() d['f2']() ``` 1. 可以当作函数的参数和返回值 ```python def f1(): print('f1') def func(argv): argv() return argv f = func(f1) f() ``` ## 闭包 ```python def func(): name = 'aaron' def inner(): print(name) return inner f = func() f() ``` 内部函数包含对外部作用域而非全剧作用域变量的引用,该内部函数称为闭包函数 判断闭包函数的方法**closure** ```python def func(): name = 'aaron' def inner(): print(name) print(inner.__closure__) return inner f = func() f() # 最后运行的结果里面有cell就是闭包 name = 'aaron' def func(): def inner(): print(name) print(inner.__closure__) return inner f = func() f() # 输出结果为None,说明不是闭包 ``` ```python def wrapper(): money = 1000 def func(): name = 'apple' def inner(): print(name,money) return inner return func f = wrapper() i = f() i() ``` ```python def func(a,b): def inner(x): return a*x + b return inner func1 = func(4,5) func2 = func(7,8) print(func1(5),func2(6)) ``` ```python from urllib.request import urlopen def func(): content = urlopen('http://myip.ipip.net').read() def get_content(): return content return get_content code = func() content = code() print(content.decode('utf-8')) content2 = code() print(content2.decode('utf-8')) ```