函数
一、定义函数:
def + 关键字(函数名,一个变量,遵循变量命名的规则) + ( 参数 ) :
(缩进) 函数体(代码块)
函数名的本质(就是这个函数所在的内存地址)像这样的就是叫第一类对象:
1. 函数名是不可以变得,函数名赋值只是分享内存地址,这个函数本身的名字是不会变的,还是原来定义时的名字
def func(): pass f = func print(func) print(f)
两次运行结果:
<function func at 0x00000000003E1E18>
<function func at 0x00000000003E1E18>
2. 函数名可以赋值:
函数名指向的是这个函数的内存地址,当把这个函数名赋值给另一个变量名,两个变量都指向函数内存地址,所以用两个变量都可以调用这一个函数内存地址,即可以调用这个函数.
def func(): print(1) f = func #赋值,令f() = func() f()
3.函数名可以作为容器类型中的一项,可以做字典的键.
def lst(): return 1,2 ,3 ,4 ,5 lsit = [*lst()] print(lsit)
4.函数名可以作为另一个函数的参数.
def login(): print('欢迎登陆') def index(auth): auth() #函数名的赋值,令auth = login,即autn() = login() print('欢迎来到首页') index(login)
5.函数可以作为另一个函数的返回值
def login_failed(): print('登录失败') def index(): print('欢迎来到首页') def login(uer,pwd): if uer == 'alex'and pwd == 'sb': return index #返回index()函数的print else: return login_failed #返回login_failed()的print res_func = login('alex','ssb') res_func()
二、调用函数:
本质是函数的内存地址加括号 ,所以函数名可以进行赋值,谁调用返回值就给谁。
三、关键字()参数)
print(关键字) #输出函数的内存地址类似于 <function f at 0x00000000003C1E18> 这样子的
函数值: 返回return #函数返回值 ,
1.没有返回值就默认返回None(即使没有写return)。
2.当程序运行碰到return时,就结束函数
3.当返回一个值的时候就是本身的数据类型,返回多个的时候就是元组形式返回。
1.函数的参数:
1.形参:在定义函数的时候使用
A.位置参数:按照实参的位置把对应的值赋值给形参中的变量
B.关键字参数:
也叫默认参数:在定义形参的时候,给变量进行赋值.后面使用的时候不用写入实参.若写实参,便重新赋值,使用实参的值.
C.混合参数:
即括号里既有位置参数也有关键字参数,这时位置参数应该在前面,默认参数在后面
2.实参: 在调用的时候使用
A.位置参数:按照位置给形参进行传参
B.关键字参数:
也叫默认参数:按照关键字给形参进行传参
C.混合参数:
即括号里既有位置参数也有关键字参数,这时位置参数应该在前面,默认参数在后面
3.传参:实参与形参在括号里是一一对应的过程就是传参
2.函数参数进阶
1.动态位置参数: * + 位置参数 #惯用(*agrs),获取的数据是一个元组,位置参数在动态位置参数前面 :位置参数 > 动态位置参数
2.动态关键字参数: ** + 关键字参数 #惯用(**kwagrs),动态关键字传参获取的是一个字典,每个默认参数都是一个键值对,关键
字参数在动态关键字参数前面: 关键字参数 > 动态关键字参数
#动态位置参数 最后获得的是以一个元组的形式输出 def eat(a,b,*food): #( * + 位置参数 )就是动态位置参数 print('我今天想吃',a,b,food) eat('回锅肉','卫龙','百威','馒头','榨菜') #动态关键字参数 最后获取的数据是一个字典 def eat(**food): # (** + 关键字参数) 是默认关键字参数 print('我今天想吃',food) eat(food=1,a=2,b=3) #混合参数 推荐这么写 位置参数>动态位置参数>默认参数>动态默认参数 def func1(a,*args,b=1,**kwargs): print(a,b,args,kwargs) def func2(a,*agrs,**kwargs,b=1): #不可以这样子用 print(a, b, agrs, kwargs) func1(a,b,args,kwargs)
总的位置关系是:
位置参数 > 动态位置参数 > 关键字参数 > 动态关键字参数
推荐写法:
def func1(a,*agrs,b=1,**kwargs): print(a, b, agrs, kwargs)
3.解包与打包:
lst = [1,2,3,4] #运行顺序: def func(*args): #聚合 (1,2,3,4) 2 print(*args) #在解包 1,2,3,4 3 func(*lst) # * 是按顺序解包相当于func(1,2,3,4) 1
dic = {'name':'alex','age':15} def func(**kwagrs): print(kwagrs) func(**dic)
4.三元运算符:
def f (a,b): l = (a if a >b else b) #三元运算符的表示方法 return l print(f(3,5))
a if a >b else b
语法: 结果 如果条件成立就是用前面的,如果条件不成立就是用后面的结果
四、函数的嵌套
注意不能出现死循环嵌套函数:
def fun2(): print(222) def fun3(): print(666) print(444) fun3() print(888) print(33) fun2() print(555)
#错误嵌套: 不能循环嵌套
def f1(): print(1) f3() def f2(): print(2) f1() def f3(): print(3) f2() f3()
五、名称空间
1.命名空间的定义
我们给存放名字和值的关系的空间起一个名字叫: 命名空间. 我们的变量在存储的时候就 是存储在这片空间中的.
命名空间分类:
1. 全局命名空间--> 我们直接在py文件中, 函数外声明的变量都属于全局命名空间:作用于全局和局部作用域,加载在运行代码之后
2. 局部命名空间--> 在函数中声明的变量会放在局部命名空间 :作用于局部作用域,加载于调用之后
3. 内置命名空间--> 存放python解释器为我们提供的名字,内置函数、list、tuple、str、int等这些都是内置命名空间:作用于全局作用域,加载于运行之后,代码之前
查找顺序: 局部空间 > 全局空间 > 内置空间
加载顺序:内置空间 > 全局空间 > 局部空间
内置函数: pytharm中自带的函数,可以直接调用的函数
2.作用域:
2.1全局作用域:全局和内置空间
.globals() 查看全局作用域的变量和内容
2.2局部作用域: 函数局部空间
.locals() 查看局部作用域内的变量和内容
#查看变量内容 a = 5 def func(): b = 3 print(b) print(locals()) #查看局部变量与内容 func() print(globals()) #查看全局变量与内容
六、global和nonlocal
1.global关键字适用于在全局作用域中
首先我们写这样一个代码,首先在全局声明一个变量,然后再局部调用这个变量,并改变这个变量的值
a = 100 def func(): global a #加了个global表示不再局部创建这个变量了.而是直接使用全局的a a = 28 print(a) func() print(a)
通过新建一个内存地址,改变变量指向来改变全局变量的内容,可变数据类型在局部可以进行直接更改,不可变的数据类型需要利用global新建一个内存地址,然后让改数据改变指向到这个新地址
例:
lst = ["麻花藤", "刘嘉玲", "詹姆斯"] def func(): lst.append("⻢云") # 对于可变数据类型可以直接进访问. 但是不能改地址.就是不能赋值 print(lst) func() print(lst)
2. nonlocal适用于局部作用域
nonlocal 表示在局部作用域中,改变离他最近的父集变量的值,调用父级命名空间中的变量。
改变离他最近的存在于局部作用域中的父集的变量:
A.即func1的变量,若func1()下没有变量,则不改变,在func1()上面就是全局作用域了
B.若func2与func1之间还有一层嵌套,则改变中间这一层的变量,说白了就是离他最近的有改变量的父集
a = 10 def func1(): a = 20 def func2(): nonlocal a #改变离他最近的父集的变量,即func1的变量,若func1()下没有 变量,则不改变,若func2与func1之间还有一层嵌套,则改变中间这一层的变量,说白了就 是离他最近的有改变量的父集
a = 30
print(a) func2() print(a) func1()
结果:
加了nonlocal
30
30
不加nonlocal
30
20