Python之函数与作用域
函数初识
-
什么是函数
获取任意一个字符串的元素的个数 s1 = 'fdskjlgfdgfdjkslgdfjkjafdsajk' count = 0 for i in s1: count += 1 print(count) # 获取列表的元素的个数 l1 = [1, 2, 3] count = 0 for i in l1: count += 1 print(count) # 面向过程编程 1. 代码重复。 2. 代码可可读性不高。 初识函数 l1 = [1, 2, 3] def new_len(): count = 0 for i in l1: count += 1 print(count) new_len() 函数:函数是以功能为导向,一个函数封装一个功能。登录,注册,文件的改的操作..... 函数减少代码的重复性,增强了代码的可读性。
-
函数的结构
l1 = [1, 2, 3] def new_len(): count = 0 for i in l1: count += 1 print(count) new_len() def 关键字: 定义一个函数。紧跟一个空格。 new_len函数名:与变量命名规范一致。一定要具有可描述性。 ():结构需要,传参使用。 : 分割符。 tab键:四个空格。缩进。函数体
-
函数的调用
l1 = [1, 2, 3] def new_len(): count = 0 for i in l1: count += 1 print(count) 函数的执行写多少次,执行多少次。 new_len() # 函数名() 函数的执行者。调用者。 new_len() # 函数名() 函数的执行者。 new_len() # 函数名() 函数的执行者。 new_len() # 函数名() 函数的执行者。 new_len() # 函数名() 函数的执行者。 l1 = [1, 2, 3] def new_len(): count = 0 for i in l1: count += 1 print(count) for i in range(10): print(111) for i in range(3): new_len()
-
函数的返回值
一个函数就是封装一个功能,这个功能一般都会有一个最终结果的,比如你写一个登录函数,最终登录成功与否是不是需要返回你一个结果?还有咱们是不是都用过len这个函数,他是获取一个对象的元素的总个数,最终肯定会返回一个元素个数这样的结果:
s1 = 'abfdas'
print(len(s1)) # 6
函数的返回值用return表示:
1.结束函数。
l1 = [1, 2, 3] def new_len(): print(111) print(222) if 1 == 1: return print(333) print(444) new_len()
2.函数中没有return或者只写一个return,函数的执行者得到的是None。
# l1 = [1, 2, 3] # def new_len(): # count = 0 # for i in l1: # count += 1 # return # print(new_len())
3.函数中return后面是单个值,函数的执行者得到的是这个值(不改变值的类型)。
def func(): print(111) # return 100 # return [1, 2, 3] return {'name': '太白'} ret = func() print(ret, type(ret))
-
函数中return后面是多个值,函数的执行者得到的是一个元组。
# def func(): # print(111) # return 1, '23期', [22, 33] # ret = func() # print(ret, type(ret)) # (1, '23期', [22, 33]) # def func(): # print(111) # # return 1, '23期', [22, 33] # a,b,c = func() # print(a,b,c) def func(): print(111) # return 1+1+2 return 2 > 1 ret = func() print(ret)
函数的传参
我们上面研究了,函数的结构,函数的执行,以及函数的返回值。对函数有一个初步的了解,那么接下来就是一个非常重要的知识点,函数的参数。函数是以功能为导向的,上面我们写的函数里面的代码都是写死的,也就是说,这个函数里面的更改起来很麻烦,试想一下,我们使用探探,陌陌等软件,可不可以进行筛选,比如选择性别,年龄等,导出结果? 再拿我们之前学过的len 这个len是不是可以获取字符串的总个数?是不是可以获取列表的总个数?你更改了len函数内部的代码了?没有吧?你看下面的例子:
s1 = 'sfdas'
l1 = [1, 3, 7]
print(len(s1)) # 5
print(len(l1)) # 3
# # 函数的传参:函数的拓展性
#
#
# def new_len(a): # 定义函数时:参数:形参。
# count = 0
# for i in a:
# count += 1
# return count
# l1 = [1, 2, 3]
# s1 = 'fdsjaklsfjgfds'
# # print(new_len(l1)) # 函数的调用者:参数 实参。
# print(new_len(s1)) # 函数的调用者:参数 实参。
# print(len(s1))
实参角度:
- 位置参数。
- 关键字参数。
- 混合参数。
形参角度:
- 位置参数。
- 默认参数。
- 动态参数。
- 仅限关键字参数。(了解)
实参角度:
-
位置参数。
# 1. 位置参数。: 从左至右,按照顺序,一一对应 def meet(sex,age,job,): print('左划一下') print('右划一下') print(f'寻找性别{sex},年龄{age}岁,{job}') print('聊天') print('约吗') print('约....') meet('女','18~25','讲师') # 写一个函数,接收两个数字的参数,将较大的数字返回。 # def comp(a,b): # if a > b: # return a # else: # return b # ret = comp(1,2000) # print(ret) # 三元运算符:简单的if else。 # a1 = 1 # b2 = 2 # ret = a1 if a1 > b2 else b2 # # print(ret) # # def comp(a,b): # # ret = a if a > b else b # # return ret # return a if a > b else b # ret = comp(1,2000) # # print(ret)
-
关键字参数。一一对应
def meet(sex,age,job,hight,weight,): print('左划一下') print('右划一下') print(f'寻找性别{sex},年龄{age}岁,身高{hight},体重{weight},工作{job}') print('聊天') print('约吗') print('约....') # meet(sex='女',job='学生',weight=120,hight=170,age='18~25')
-
混合参数。关键字参数一定在位置参数后面,一一对应。
# def meet(sex,age,job,hight,weight,): # print('左划一下') # print('右划一下') # print(f'寻找性别{sex},年龄{age}岁,身高{hight},体重{weight},工作{job}') # print('聊天') # print('约吗') # print('约....') # # meet('男',27,'ITC语言',weight=120,hight=175,)
形参角度:
-
位置参数:与实参角度位置参数一样。
# def meet(sex,age,job): # print('左划一下') # print('右划一下') # print(f'寻找性别{sex},年龄{age}岁,{job}') # print('聊天') # print('约吗') # print('约....') # meet('女','18~25','讲师')
-
默认参数: 一定在位置参数后面,不传参数即沿用默认的参数。
# open('文件的改',encoding='utf-8') def meet(age,job,sex='女'): print('左划一下') print('右划一下') print(f'寻找性别{sex},年龄{age}岁,{job}') print('聊天') print('约吗') print('约....') # meet('18~25','幼师') # 更改默认参数 # meet('18~25','幼师',sex='laddy_boy') meet('18~25','幼师','laddy_boy')
函数进阶
-
函数的传参:形参角度:第三种传参方式。
动态参数: *args **kwargs
# def eat(food1,food2,food3): # print(f'我请你吃:{food1},{food2},{food3}') # eat('蒸羊羔','蒸熊掌','蒸鹿尾') # 当给函数传入的参数数目不定时,之前的传参方式解决不了问题。 # 万能参数,动态参数。 *args def eat(food1,food2,food3): print(f'我请你吃:{food1},{food2},{food3}') eat('蒸羊羔','蒸熊掌','蒸鹿尾','烧花鸭','烧企鹅') def eat(*args): # 将实参角度:定义一个函数时,* 所有的位置参数聚合到一个元组中。 print(args) print(f'我请你吃:{args}') eat('蒸羊羔','蒸熊掌','蒸鹿尾','烧花鸭','烧企鹅') # **kwargs def func(**kwargs): # 函数的定义时:**将实参角度所有的关键字参数聚合成了一个字典,给了kwargs. print(kwargs) func(name='alex',age=84,hobby='唱跳rap篮球') # *args,**kwargs 万能参数 def func(*args,**kwargs): print(args,kwargs)
练习:写一个函数,求传入函数中的不定个数的数字实参的和。
def func(lis): num1 = 0 for i in lis: num1 += i return num1
-
*的魔性用法
# 函数中 def func(*args,**kwargs): print(args) # (1, 2, 3,'太白', 'wusir', '景女神') print(kwargs) l1 = [1, 2, 3] l2 = ['太白', 'wusir', '景女神'] # func(l1,l2) # func(*l1,*l2) # 当函数的执行时:*iterable 代表打散。 func(*[1, 2, 3],*(11,22),*'fdsakl') # 当函数的执行时:*iterable 代表打散。 def func(*args,**kwargs): print(args) print(kwargs) func(**{'name':"alex"},**{'age': 73,'hobby': '吹'}) #当函数的执行时:**dict 代表打散。 # 函数外:处理剩余元素 a,b,*c = [1,2,3,4,5] a,*c,b, = [1,2,3,4,5] a,*c = range(5) a,*c,b = (1,2,3,4,5,6) print(a,c,b)
-
形参角度的最终顺序
*args的位置 *args不能放在位置参数前面,a,b取不到值 def func(*args,a,b,sex='man',): print(a) print(b) print(sex) print(args) # print(kwargs) func(1,2,4,5,6) args如果想要接收到值之前,肯定要改变sex默认参数。 def func(a,b,sex='man',*args): print(a) print(b) print(sex) print(args) # print(kwargs) func(1,2,4,5,6) def func(a,b,*args,sex='man'): print(a) print(b) print(sex) print(args) # print(kwargs) func(1,2,4,5,6) func(1,2,4,5,6,sex='women') **kwargs 位置参数,*args,默认参数,**kwargs def func(a,b,*args,sex='man',**kwargs,): print(a) print(b) print(sex) print(args) print(kwargs) func(1,2,4,5,6,name='太白',age=18)
-
函数的传参:形参角度:第四种传参方式(了解)
位置参数,*args,默认参数,仅限关键字参数,**kwargs def func(a,b,*args,sex='man',c,**kwargs,): print(a) print(b) print(sex) print(c) print(args) print(kwargs) func(1,2,4,5,6,67,c=666,name='太白',age=18,)
函数的作用域
-
从空间角度研究函数
全局名称空间: py文件运行时开辟的,存放的是执行的py文件(除去函数内部)的所有的变量与值(地址)的对应关系,整个py文件结束之后,才会消失。
临时(局部)名称空间: 函数执行时,在内存中临时开辟的一个空间,存放的函数中的变量与值的对应关系,随着函数的结束而消失。
内置名称空间:input,print,内置函数等。
![V05C~N6C[ECT6X11%CC876
D2R]MZMF($O.png)
-
取值顺序加载顺序
加载顺序:上面这三个空间,谁先加载到内存。
内置名称空间 ----》 全局名称空间 ----》 (函数执行时)临时名称空间
取值顺序:(就近原则)
-
作用域
全局作用域:全局名称空间,内置名称空间。
局部作用域:局部名称空间。
-
内置函数:globals,locals
""" 此文件研究的是内置函数 globals locals """ # name = 'alex' # l1 = [1, 2, 3] # # def func(): # age = '18' # # print(globals()) # 全局作用域所有的内容 # print(locals()) # 当前位置 # name = 'alex' # l1 = [1, 2, 3] # def func(): # age = '18' # oldboy = '老男孩教育' # print(globals()) # 全局作用域所有的内容 # print(locals()) # 当前位置的变量与值的对应关系 # # func() # name = 'alex' # l1 = [1, 2, 3] # # def func(): # age = '18' # oldboy = '老男孩教育' # def inner(): # name_class = 'python23期' # print(globals()) # 全局作用域所有的内容 # print(locals()) # 当前位置的变量与值的对应关系 # inner() # func()
-
高阶函数(嵌套函数)
# 例1: # def func1(): # print('in func1') # print(3) # def func2(): # print('in func2') # print(4) # func1() # print(1) # func2() # print(2) ''' in func1 3 1 in func2' 4 2 ''' # 例2: def func1(): print('in func1') print(3) def func2(): print('in func2') func1() print(4) print(1) func2() print(2) ''' 1 in func2 in func1 3 4 2 ''' # # 例3: # def fun2(): print(2) def func3(): print(6) print(4) func3() print(8) print(3) fun2() print(5) ''' 3 2 4 6 8 5 '''
关键字:global,nonlocal
# count = 0
#
# def func():
# count += 1
# func()
# UnboundLocalError: local variable 'count' referenced before assignment
# 解释器认为:如果你在局部作用域对一个变量进行修改了,
# 你在局部作用域已经定义好这个变量了。
global
-
可以在局部作用域声明一个全局变量。
# 这是剪切 # def func(): # # global name # name = 1 # # print(globals()) # # print(locals()) # name += 1 # print(globals()) # # # func() # # print(name) # print(globals())
-
可以修改全局变量。
# count = 0 # # def func(): # global count # count += 1 # # print(count) # func() # print(count)
nonlocal
- 不能操作全局变量。
- 可以对父级作用域的变量进行修改,并且在当前作用域创建(复制)一份此变量。
# 这是复制
# def func():
# count = 0
# def inner():
# nonlocal count
# count += 1
# print(count)
# print(locals())
# inner()
# print(locals())
# func()
# UnboundLocalError: local variable 'count' referenced before assignment
# 解释器认为:如果你在局部作用域对一个变量进行修改了,
# 你在局部作用域已经定义好这个变量了。
默认参数的坑
# def func(a,b=False):
# print(a)
# print(b)
# func(1,True)
# 当你的默认参数如果是可变的数据类型,你要小心了。
# def func(a,l=[]):
# l.append(a)
# return l
# print(func(1)) # [1,]
# print(func(2)) # [2,]
# print(func(3)) # [3,]
函数名的应用
- 函数名的内存地址
# a = 1
# b = 2
# c = a + b
# print(c)
# def func():
# print(666)
#
# print(func) # func = <function func at 0x00000000003F1EA0>
# 1,函数名指向的是函数的内存地址,加上()就执行这个函数。
# func()
- 函数名可以作为一个变量赋值给其他变量
# age1 = 12
# age2 = age1
# age3 = age2
# print(age3)
# def func():
# print(666)
#
# f1 = func
# f2 = f1
# f2()
# f1()
- 函数名可以作为容器类类型的元素。
# a = 1
# b = 2
# c = 3
# l = [a,b,c]
# print(l)
# def func1():
# print('in func1')
#
# def func2():
# print('in func2')
#
# def func3():
# print('in func3')
#
# l = [func1, func2, func3]
# # print(l)
# for i in l:
# i()
- 函数名可以作为函数的实参。
# a = 1
# def func(argv):
# print(argv)
# func(a)
# def func1():
# print('in func1')
#
# def func2(argv):
# argv()
# print('in func2')
#
# func2(func1)
- 函数名可以作为函数的返回值
# b = 666
#
# def func1():
# print('in func1')
#
# def func2(argv):
# print('in func2')
# return argv
# ret = func2(b)
# print(ret)
# def func1():
# print('in func1')
#
# def func2(argv):
# # argv = func1 : function 43543
# print('in func2')
# return argv
# ret = func2(func1) # func1 : function 43543
# ret()
# def wrapper():
#
# def inner():
# print(666)
# return inner
# # ret = wrapper()
# # ret()
# wrapper()()
python新特性:f-string格式化输出
# 1,不区分大小写
# num = input('>>>')
# s = F'python{num}'
# print(s)
# 可以加入表达式
# s1 = 'alex'
# s2 = f'我的名字{s1.upper()}'
# print(s2)
# l1 = ['太白', 18, 176]
# s1 = f'我的名字{l1[0]},我的年龄{l1[1]},我的身高{l1[2]}'
# print(s1)
# 可以结合函数
# def func(a,b):
# return a + b
# s1 = f'最终的结果{func(1,3)}'
# print(s1)
# 不能放一些特殊的字符 ! , : { } ;
# print(f"替换结果{{{73}}}")
# print(f'{12,}')