Python基础(十)
今日主要内容
- 补充:传参与参数分配区别
- 动态传参
- 函数注释
- 名称空间
- 函数名的使用
- 函数嵌套
- global和nonlocal
补充:传参与参数分配区分
先看一个函数定义和函数调用
def func(a, b):
print(b, a) a = 5
b = 10
func(b, a) # 5 10

- 粉色箭头是传参的过程
- 青色箭头是分配参数的过程
传参的过程按照参数类型进行,位置参数按顺序一一对应传参,与变量名是什么无关
而参数分配是函数自己的事,函数体属于函数,分配参数的过程要按照变量名分配
一、动态传参
(一) 为什么要用动态传参
先来看一个例子:
- 定义一个吃饭的函数,每次调用时传入要吃的东西,打印菜单
def eat(zhushi, huncai, sucai, tang, tiandian):
print("我要吃:", zhushi, huncai, sucai, tang, tiandian) eat("大米饭", "红烧肉", "烧茄子", "番茄汤", "慕斯")
# 我要吃: 米饭 红烧肉 烧茄子 番茄汤 慕斯
- 这可能是我的饭量,但是这时来了一个女生,女生吃不了这么多啊,只选了一个小花卷,一盘黄瓜炒鸡蛋
def eat(zhushi, huncai, sucai, tang, tiandian):
print("我要吃:", zhushi, huncai, sucai, tang, tiandian) eat("小花卷", "黄瓜炒鸡蛋")
# TypeError: eat() missing 3 required positional arguments: 'sucai', 'tang', and 'tiandian'
- 由于我填入的参数与定义的参数数量不对应,调用时就会报错,怎么解决这个事情呢,这就要用到了我们的动态传参。
动态传参可以给函数传入不定量的参数,以元组、字典的方式打包起来,这就解决了我们填入参数是数量不定引发的error,下面我们来看看动态传参具体的方法
(二)两类动态传参
- 动态位置参数
- 动态关键字参数
动态位置参数(动态接收位置参数)
- 先来回忆一下位置参数,按照一一对应的位置来传参
def eat(zhushi, tang): # 按照位置接收参数(形参)
print(f"主食:{zhushi} 汤:{tang}") eat("米饭", "番茄蛋花汤") # 位置参数(实参)
# 主食:米饭 汤:番茄蛋花汤
- 动态接收位置参数,在形参前加一个星号
*,表示可接收任意数量的形参,以元组存储
def eat(*food): # 参数前加一个星号,表示动态接收位置参数
print(food) eat("米饭", "番茄蛋花汤") # 可以填写任意数量参数
# ('米饭', '番茄蛋花汤')
- 动态位置参数在python中一般用
*args来表示,只是一个行业内的规范,也可以用其他变量名定义但不建议使用
def eat(*args): # 一般用*args表示动态位置参数
print(args) eat("米饭", "番茄蛋花汤") # 可以填写任意数量参数
# ('米饭', '番茄蛋花汤')
结合之前两种形参(位置参数、默认值参数)共同分析
位置参数 + 动态位置参数
- 位置参数必须在动态位置参数前(位置参数 > 动态位置参数)
def eat(zhushi, *food):
print(zhushi, food) eat("米饭", "红烧肉", "排骨", "烧鸡")
# 米饭 ('红烧肉', '排骨', '烧鸡')
- 原因:如果动态位置参数(带星的)在前,那么所有实参全部都传给了动态位置参数(带星的),后面的位置参数(不带星的)是接收不到任何实参的,导致报错
def eat(*food, zhushi):
print(food, zhushi) eat("米饭", " 红烧肉", "排骨", "烧鸡")
# TypeError: eat() missing 1 required keyword-only argument: 'zhushi'
默认值参数 + 动态位置参数
- 默认参数必须在动态位置参数后( 动态位置参数 > 默认值参数)
def eat(*food, zhushi="米饭"):
print(food, zhushi) eat("回锅肉", "红烧肉", "翡翠白玉汤")
# ('回锅肉', '红烧肉', '翡翠白玉汤') 米饭- 原因:默认值参数的目的就是为了在重复输入情况下使用默认值,省去每次输入一样的参数,若默认值参数在前,则每次调用都需要填入参数,默认值参数就没有意义了
def eat(zhushi="米饭", *food):
print(zhushi, food) eat("米饭", "回锅肉", "红烧肉", "翡翠白玉汤") # 默认值参数没有意义
# 米饭 ('回锅肉', '红烧肉', '翡翠白玉汤')位置参数 + 默认值参数 + 动态位置参数
- 参数顺序:位置参数 > 动态位置参数 > 默认值参数
def eat(zhushi, *food, tang="番茄蛋花汤"):
print(zhushi, food, tang) eat("米饭", "红烧肉", "排骨", "烧鸡")
# 米饭 ('红烧肉', '排骨', '烧鸡') 番茄蛋花汤
动态关键字参数(动态接收关键字参数)
- 星号
*可以接收任意数量的位置参数,但是无法接收任意数量的关键字参数,在形参前加两个星号**就可以接受任意数量的关键字参数,以字典存储
def eat(zhushi, **cai):
print(f"主食:{zhushi} 菜:{cai}") eat("米饭", cai1="红烧肉", cai2="可乐鸡翅")
主食:米饭 菜:{'cai1': '红烧肉', 'cai2': '可乐鸡翅'}- 动态关键字参数在python中一般用
**kwargs来表示,只是一个行业内的规范,也可以用其他变量名定义但不建议使用
def eat(zhushi, **kwargs):
print(f"主食:{zhushi} 菜:{kwargs}") eat("米饭", cai1="红烧肉", cai2="可乐鸡翅")
主食:米饭 菜:{'cai1': '红烧肉', 'cai2': '可乐鸡翅'}- 星号
结合之前三种形参(位置参数、动态位置参数、默认值参数)共同分析
- 回想一下实参的顺序,给入实参时,位置参数必须要在关键字参数前,否则会报错
def func(a, b, c):
print(a, b, c) func(1, b=2, 3)
# SyntaxError: positional argument follows keyword argument- 所以关键字参数必须要在位置参数后,由于实参是这个顺序,所以形参在接收的时候也必须是这个顺序,所以动态关键字参数也必须在动态位置参数后
def eat(zhushi, *args, tang="番茄蛋花汤", **kwargs):
print(f"主食:{zhushi} 甜品:{args} 汤:{tang} 菜:{kwargs}") eat("米饭", "慕斯", "布丁", recai="可乐鸡翅", liangcai="大拌菜")
# 主食:米饭 甜品:('慕斯', '布丁') 汤:番茄蛋花汤 菜:{'recai': '可乐鸡翅', 'liangcai': '大拌菜'}形参的最终顺序:(重要)
- 位置参数 > 动态位置参数 > 默认值参数 > 动态关键字参数
星号
*的作用- 在函数定义的时候:聚合
- 动态位置参数聚合成元组
- 动态关键字参数聚合成字典
def func(a, b, *args): # 将我传入的3,4,5聚合成一个元组
print(a, b, args) func(1, 2, 3, 4, 5) # 1 2 (3, 4, 5)- 在函数调用的时候:打散
- 动态位置参数打散获取的是单个元素
- 动态关键字参数打散获取的是字典的键
def func(a, b, *args): # 将我传入的3,4,5聚合成一个元组
print(a, b, *args) # 将元组打散成元素输出 func(1, 2, 3, 4, 5) # 1 2 3 4 5无敌传参
- 可以传入任意类型参数、任意数量参数,无敌
def func(*args, **kwargs):
print(args, kwargs) func(1, 2, 3, k1=v1, k2=v2)
# (1, 2, 3) {'k1': 'v1', 'k2': 'v2'}
二、函数注释
(一)函数注释的好处
- 代码永远是写给人看的,在函数中添加注释可以让人清楚明了的了解函数的作用以及函数的用法
- 防止自己遗忘自己写的函数是干什么用的,当忘记的时候看注释就好了
- 可以在注释中明确变量以及返回值建议的数据类型
(二)注释用法
在函数体的第一行用多行注释
""" """,pycharm在函数中添加注释默认出现函数所用到的所有变量def func(args1, args2):
""" :param args1: int
:param args2: int
:return: int
"""
print(args1)
print(args2)
return args1, args2
在函数定义的时候,形参后面可以加冒号并写出建议传入的数据类型
def func(args1: int, args2: int):
"""
此处添加注释内容
:param args1: int
:param args2: int
:return: int
"""
print(args1)
print(args2)
return args1, args2
(三)关于函数的两个查看方法
查看函数的注释
函数名.__doc__,若无注释返回None
def func(args1, args2):
"""
进行加法运算
:param args1: int
:param args2: int
:return: int
"""
return args1 + args2 print(func.__doc__)运行结果: 进行加法运算
:param args1: int
:param args2: int
:return: int查看函数的名字
函数名.__name__
def func(args1, args2):
"""
进行加法运算
:param args1: int
:param args2: int
:return: int
"""
return args1 + args2 add = func
print(add.__name__) # 运行结果:func
三、名称空间(命名空间)
(一)什么是名称空间
- Python解释器开始执行之后,就会在内存中开辟了一个空间,每当遇到一个变量的时候,就会把变量名(内存地址)和值之间的关系记录下来,但是当遇到函数定义的时候,解释器只是把函数名(函数的内存地址)读入内存,表示这个函数存在了,而函数内部的变量和逻辑,解释器是不关心的,也就是说一开始的时候函数只是加载进来了,仅此而已,只有当函数被调用和访问的时候,解释器才会根据函数内部声明的变量开进行开辟变量的内部空间,随着函数执行完毕,这些函数内部变量占用的空间也会随着函数执行完毕被清空,我们给存放名字和值的关系的空间起一个名字叫:名称空间(命名空间),我们的变量在存储的时候就是存储在这片空间中的。(出自邱彦涛老师,我的偶像)
(二)名称空间的分类
- 在python中名称空间分为三部分:
- 内置空间
- 全局空间
- 局部空间
内置空间:存放python解释器为我们提供的名字,len、print、global等等
全局空间:用来存放py文件顶格运行时声明的变量
局部空间:用来存放在函数运行时声明的变量
名称空间加载顺序:
- 内置名称空间 > 全局名称空间 > 局部名称空间

变量取值顺序:
- 局部名称空间 > 全局名称空间 > 内置名称空间

(三)作用域
- 作用域就是作用范围,作用域分为两类:
- 全局作用域
- 局部作用域
全局作用域:整个文件的任何位置都可以使用
- 包含:内置名称空间 + 全局名称空间
globals()函数可以查看全局作用域中的变量和函数信息
a = 10
b = 20 def func(args1, args2):
print(args1)
print(args2)
print(globals())
return None func(30, 40)运行结果:
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001B61F2786D8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/python_S26/day10/exercise.py', '__cached__': None, 'a': 10, 'b': 20, 'func': <function func at 0x000001B61D671EA0>}局部作用域:在函数内部可以使用
- 包含:局部名称空间
locals()函数可以查看当前作用域中的变量和函数信息(建议查看局部)
a = 10
b = 20 def func(args1, args2):
print(args1)
print(args2)
print(locals())
return None func(30, 40)运行结果:
30
40
{'args2': 40, 'args1': 30}
四、函数名的使用
(一)作为值
函数名可以当作值,赋值给另一个变量
def func():
print("zxd") name = func # 函数名赋值给另一个变量
print(func)
print(name)
name() 运行结果:
<function func at 0x000002161A261EA0>
<function func at 0x000002161A261EA0>
zxd
(二)作为参数
函数名可以当作另一个函数的参数来使用
def func():
print("zxd") def name(args):
print(args) # <function func at 0x00000299B5811EA0>
args() name(func) 运行结果:
<function func at 0x00000299B5811EA0>
zxd
(三)作为返回值
函数名可以当作另一个函数的返回值来使用
def name():
print("zxd") def func():
return name func()() 运行结果:
zxd
(四)作为容器中的元素
函数名可以当作元素存储在容器中
def login():
print("登录") def register():
print("注册") def look():
print("浏览商品") def buy():
print("购买商品") msg = """
1.注册
2.登录
3.浏览商品
4.购买商品
请选择序号:""" dic_func = {
"1": register,
"2": login,
"3": look,
"4": buy,
}
while True:
num = input(msg)
if num in dic_func.keys():
dic_func[num]()
else:
print("输入错误!")
五、函数嵌套
(一)交叉嵌套
函数参数作为另一个函数的参数,从而插入另一个函数中
看一个例子:
def func1(a):
print(1)
a()
print(2) def func2():
print(3)
re = func3()
print(re)
print(4) def func3():
print(5) func1(func2) 运行结果:
1 3 5 None 4 2运行顺序

(二)内部嵌套
函数内部嵌套函数
看一个例子
def func1():
print(1)
def func2():
print(2)
def func3():
print(3)
func3()
print(4)
func2()
print(5) func1() 运行结果:
1 2 3 4 5运行顺序

六、global和nonlocal
(一)global
全局中的变量在函数内部只有使用权,可以拿来用,但是不能更改
num = 10
def func():
num = num + 1
print(num) func()
print(num)
# UnboundLocalError: local variable 'num' referenced before assignment如果想在局部中修改全局变量,必须先用global声明要修改的全局变量
num = 10
def func():
global num
num = num + 1
print(num) # 11 func()
print(num) # 11在函数中使用了global声明了变量,但全局空间中并没有这个变量时,global会在全局空间中开辟这个变量
def func():
global num
num = 10
print(num) # 10 func()
print(num) # 10总结:
- 可以在局部空间修改全局空间的是变量
- 若全局空间无声明的变量则创建该变量
- global有效的控制因误操作在局部空间修改全局空间的变量
(二)nonlocal
nonlocal修改离nonlocal最近的上一层名称空间的变量,但只修改局部空间中的变量
num = 10
def func1():
num = 20
def func2():
num = 30
def func3():
nonlocal num
num += 1
print(num)
func3()
print(num)
func2()
print(num) func1()
print(num) 运行结果:
31 31 20 10nonlocal声明的变量如果是全局空间中的变量就会报错,并且nonlocal不会创建变量
num = 10
def func1():
def func2():
def func3():
nonlocal num # 局部空间内没有变量num
print(num)
func3()
print(num)
func2()
print(num) func1()
print(num) 运行结果:
SyntaxError: no binding for nonlocal 'num' found当前空间如果有变量,在去用nonlocal声明该变量,会报错,nonlocal只能声明上一层名称空间的变量
num = 10
def func1():
def func2():
def func3():
num = 10
nonlocal num #
print(num)
func3()
print(num)
func2()
print(num) func1()
print(num) 运行结果:
SyntaxError: name 'num' is assigned to before nonlocal declaration
Python基础(十)的更多相关文章
- python 基础(十) 面向对象
面向对象 一.概念 类(class): 用来描述具有相同属性和方法的对象的集合 对象是类的实例化 类变量:类变量在整个实例化的对象中是共用的.定义在类中 并且是函数体外的 实例变量:只能作用于 当前类 ...
- Python基础(十) __init__与__new__区别
__init__与__new__区别: __init__在python,其实是,在实例化之后执行的,用来初始化一些属性,相当于构造函数,但是又不一样 细心一些,通过参数会有所发现,其实__init__ ...
- Python基础(十二) 类私有成员和保护成员
python中的protected和private python中用 _var :变量名前一个下划线来定义,此变量为保护成员protected,只有类及其子类可以访问.此变量不能通过from XXX ...
- python基础十五之递归函数
递归函数,在函数中调用自身函数,就会形成一个递归函数.例如: def recursion(n): n += 1 print(n) recursion(n) 由于递归函数的结构,在函数调用时,它会一直调 ...
- python 基础(十五) socket编程
SOCKET TCP协议: 有请求 有响应 称之为 tcp协议 是面向连接的协议 就是在收发数据之前 必须先要建立一个可靠的链接 三次握手 如:网站 UDP协议: 是一个非链接的协议 传输之前不需要键 ...
- python 基础(十四) 正则表达式
正则表达式 概念: 正则匹配就是一个模糊的匹配 只要符合我的匹配规则 就会认为是正确的数据(精确的匹配) 1.[] #代表原子表把想要匹配的内容写入原子表中 匹配包含的任意一位字符 [a] ...
- python 基础(十二) 图片简单处理
pillow 图片处理模块 安装 pip install pillow pip是安装第三方模块的工具 缩放图片实例 from PIL import Image path = r'C:\Users\x ...
- python基础十四之匿名函数
匿名函数 处理简单问题的简化函数,关键字lambda. # 格式:函数名 = lambda 参数:返回值 anonymity = lambda s: s ** 0.5 print(anonymity( ...
- python基础十二之生成器进阶
生成器表达式 (满足条件的元素或其相关的操作 for 元素 in 可迭代对象 if 条件) g = (i for i in range(10)) for i in g: print(i) egg_li ...
- python基础十之装饰器
1,装饰器的形成 编程原则:开放封闭原则. 开放:对扩展是开放的 封闭:对修改是封闭的 因为修改是封闭的,所以为了对函数进行功能的扩展,就使用装饰器! 2,装饰器的定义 # wrapper就是一个装饰 ...
随机推荐
- 某团面试题:JVM 堆内存溢出后,其他线程是否可继续工作?
转载注明:http://dwz.win/gHc 最近网上出现一个美团面试题:"一个线程OOM后,其他线程还能运行吗?".我看网上出现了很多不靠谱的答案.这道题其实很有难度,涉及的知 ...
- 从零写一个编译器(十三):代码生成之遍历AST
项目的完整代码在 C2j-Compiler 前言 在上一篇完成对JVM指令的生成,下面就可以真正进入代码生成部分了.通常现代编译器都是先把生成IR,再经过代码优化等等,最后才编译成目标平台代码.但是时 ...
- linux安装杀软 clamAV
ClamAV 是Linux平台最受欢迎的杀毒软件,ClamAV 属于免费的开源软件,支持多种平台.ClamAV是基于病毒扫描的命令行工具,但同时也有支持图形界面的ClamTK工具.ClamAV 主要用 ...
- restapi(6)- do it the functional way, 重温函数式编程
再次看了看上篇博客的源代码,发现连自己都看不懂了.想是为了赶时间交货不知不觉又回到OOP行令模式了,看看下面这段代码: (post & parameters('pid,'desc.?,'wid ...
- Java生产者消费者的三种实现
Java生产者消费者是最基础的线程同步问题,java岗面试中还是很容易遇到的,之前没写过多线程的代码,面试中被问到很尬啊,面完回来恶补下.在网上查到大概有5种生产者消费者的写法,分别如下. 用sync ...
- HTML+JavaScript自己动手做日历
当我们需要在页面中显示某月的事项,或是选择某一段日期时,常常要使用到日历组件.这一组件同样有着许多现成的类库,然而亲自动手开发一个日历,从中了解其实现原理也是非常必要的.在本例中我们就将制作一款非常经 ...
- SpringBoot读取配置文件源码探究
1. SpringBoot读取配置文件源码探究 1.1. 概览 springboot的源码是再原来的Spring源码上又包了一层,看过spring源码都知道,当我们从入口debug进去的时候,原来的S ...
- HTML(四)图像,表格
HTML 图像 插入动图的语法和静态图的语法是一样的 HTML 图像- 图像标签( )和源属性(Src) 一个来自文件夹中的图像: 一个来自网站的图像: alt 属性用来为图像定义一串预备的可替换的文 ...
- HDU-3478Catch二分图的否命题
HDU-3478Catch 题意:考虑Thief能否: 由于我推着推着就想到必须要三点可以互通,和二分图的结论正好相反,所以就试了一发, 真没想到thief的初始位置是不用考虑的. 下面是ac代码: ...
- 2019 Multi-University Training Contest 5
2019 Multi-University Training Contest 5 A. fraction upsolved 题意 输入 \(x,p\),输出最小的 \(b\) 使得 \(bx\%p&l ...