今日主要内容

  • 补充:传参与参数分配区别
  • 动态传参
  • 函数注释
  • 名称空间
  • 函数名的使用
  • 函数嵌套
  • 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,下面我们来看看动态传参具体的方法

(二)两类动态传参

  • 动态位置参数
  • 动态关键字参数
  1. 动态位置参数(动态接收位置参数)

    • 先来回忆一下位置参数,按照一一对应的位置来传参
    def eat(zhushi, tang):  # 按照位置接收参数(形参)
    print(f"主食:{zhushi} 汤:{tang}") eat("米饭", "番茄蛋花汤") # 位置参数(实参)
    # 主食:米饭 汤:番茄蛋花汤
    • 动态接收位置参数,在形参前加一个星号*,表示可接收任意数量的形参,以元组存储
    def eat(*food):  # 参数前加一个星号,表示动态接收位置参数
    print(food) eat("米饭", "番茄蛋花汤") # 可以填写任意数量参数
    # ('米饭', '番茄蛋花汤')
    • 动态位置参数在python中一般用*args来表示,只是一个行业内的规范,也可以用其他变量名定义但不建议使用
    def eat(*args):  # 一般用*args表示动态位置参数
    print(args) eat("米饭", "番茄蛋花汤") # 可以填写任意数量参数
    # ('米饭', '番茄蛋花汤')
  2. 结合之前两种形参(位置参数、默认值参数)共同分析

    • 位置参数 + 动态位置参数

      • 位置参数必须在动态位置参数前(位置参数 > 动态位置参数)
      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("米饭", "红烧肉", "排骨", "烧鸡")
      # 米饭 ('红烧肉', '排骨', '烧鸡') 番茄蛋花汤
  3. 动态关键字参数(动态接收关键字参数)

    • 星号*可以接收任意数量的位置参数,但是无法接收任意数量的关键字参数,在形参前加两个星号**就可以接受任意数量的关键字参数,以字典存储
    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': '可乐鸡翅'}
  4. 结合之前三种形参(位置参数、动态位置参数、默认值参数)共同分析

    • 回想一下实参的顺序,给入实参时,位置参数必须要在关键字参数前,否则会报错
    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': '大拌菜'}
  5. 形参的最终顺序:(重要)

  • 位置参数 > 动态位置参数 > 默认值参数 > 动态关键字参数
  1. 星号*的作用

    • 在函数定义的时候:聚合
    • 动态位置参数聚合成元组
    • 动态关键字参数聚合成字典
    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
  2. 无敌传参

    • 可以传入任意类型参数、任意数量参数,无敌
    def func(*args, **kwargs):
    print(args, kwargs) func(1, 2, 3, k1=v1, k2=v2)
    # (1, 2, 3) {'k1': 'v1', 'k2': 'v2'}

二、函数注释

(一)函数注释的好处

  • 代码永远是写给人看的,在函数中添加注释可以让人清楚明了的了解函数的作用以及函数的用法
  • 防止自己遗忘自己写的函数是干什么用的,当忘记的时候看注释就好了
  • 可以在注释中明确变量以及返回值建议的数据类型

(二)注释用法

  1. 在函数体的第一行用多行注释""" """,pycharm在函数中添加注释默认出现函数所用到的所有变量

    def func(args1, args2):
    """ :param args1: int
    :param args2: int
    :return: int
    """
    print(args1)
    print(args2)
    return args1, args2

  2. 在函数定义的时候,形参后面可以加冒号并写出建议传入的数据类型

    def func(args1: int, args2: int):
    """
    此处添加注释内容
    :param args1: int
    :param args2: int
    :return: int
    """
    print(args1)
    print(args2)
    return args1, args2

(三)关于函数的两个查看方法

  1. 查看函数的注释

    • 函数名.__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
  2. 查看函数的名字

    • 函数名.__name__
    def func(args1, args2):
    """
    进行加法运算
    :param args1: int
    :param args2: int
    :return: int
    """
    return args1 + args2 add = func
    print(add.__name__) # 运行结果:func

三、名称空间(命名空间)

(一)什么是名称空间

  • Python解释器开始执行之后,就会在内存中开辟了一个空间,每当遇到一个变量的时候,就会把变量名(内存地址)和值之间的关系记录下来,但是当遇到函数定义的时候,解释器只是把函数名(函数的内存地址)读入内存,表示这个函数存在了,而函数内部的变量和逻辑,解释器是不关心的,也就是说一开始的时候函数只是加载进来了,仅此而已,只有当函数被调用和访问的时候,解释器才会根据函数内部声明的变量开进行开辟变量的内部空间,随着函数执行完毕,这些函数内部变量占用的空间也会随着函数执行完毕被清空,我们给存放名字和值的关系的空间起一个名字叫:名称空间(命名空间),我们的变量在存储的时候就是存储在这片空间中的。(出自邱彦涛老师,我的偶像)

(二)名称空间的分类

  • 在python中名称空间分为三部分:

    • 内置空间
    • 全局空间
    • 局部空间
  1. 内置空间:存放python解释器为我们提供的名字,len、print、global等等

  2. 全局空间:用来存放py文件顶格运行时声明的变量

  3. 局部空间:用来存放在函数运行时声明的变量

  4. 名称空间加载顺序:

    • 内置名称空间 > 全局名称空间 > 局部名称空间

  5. 变量取值顺序:

    • 局部名称空间 > 全局名称空间 > 内置名称空间

(三)作用域

  • 作用域就是作用范围,作用域分为两类:

    • 全局作用域
    • 局部作用域
  1. 全局作用域:整个文件的任何位置都可以使用

    • 包含:内置名称空间 + 全局名称空间
    • 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>}
  2. 局部作用域:在函数内部可以使用

    • 包含:局部名称空间
    • 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 10
  • nonlocal声明的变量如果是全局空间中的变量就会报错,并且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基础(十)的更多相关文章

  1. python 基础(十) 面向对象

    面向对象 一.概念 类(class): 用来描述具有相同属性和方法的对象的集合 对象是类的实例化 类变量:类变量在整个实例化的对象中是共用的.定义在类中 并且是函数体外的 实例变量:只能作用于 当前类 ...

  2. Python基础(十) __init__与__new__区别

    __init__与__new__区别: __init__在python,其实是,在实例化之后执行的,用来初始化一些属性,相当于构造函数,但是又不一样 细心一些,通过参数会有所发现,其实__init__ ...

  3. Python基础(十二) 类私有成员和保护成员

    python中的protected和private python中用 _var :变量名前一个下划线来定义,此变量为保护成员protected,只有类及其子类可以访问.此变量不能通过from XXX ...

  4. python基础十五之递归函数

    递归函数,在函数中调用自身函数,就会形成一个递归函数.例如: def recursion(n): n += 1 print(n) recursion(n) 由于递归函数的结构,在函数调用时,它会一直调 ...

  5. python 基础(十五) socket编程

    SOCKET TCP协议: 有请求 有响应 称之为 tcp协议 是面向连接的协议 就是在收发数据之前 必须先要建立一个可靠的链接 三次握手 如:网站 UDP协议: 是一个非链接的协议 传输之前不需要键 ...

  6. python 基础(十四) 正则表达式

    正则表达式 概念: 正则匹配就是一个模糊的匹配 只要符合我的匹配规则 就会认为是正确的数据(精确的匹配) 1.[] #代表原子表把想要匹配的内容写入原子表中   匹配包含的任意一位字符 [a]     ...

  7. python 基础(十二) 图片简单处理

    pillow 图片处理模块 安装 pip install pillow  pip是安装第三方模块的工具 缩放图片实例 from PIL import Image path = r'C:\Users\x ...

  8. python基础十四之匿名函数

    匿名函数 处理简单问题的简化函数,关键字lambda. # 格式:函数名 = lambda 参数:返回值 anonymity = lambda s: s ** 0.5 print(anonymity( ...

  9. python基础十二之生成器进阶

    生成器表达式 (满足条件的元素或其相关的操作 for 元素 in 可迭代对象 if 条件) g = (i for i in range(10)) for i in g: print(i) egg_li ...

  10. python基础十之装饰器

    1,装饰器的形成 编程原则:开放封闭原则. 开放:对扩展是开放的 封闭:对修改是封闭的 因为修改是封闭的,所以为了对函数进行功能的扩展,就使用装饰器! 2,装饰器的定义 # wrapper就是一个装饰 ...

随机推荐

  1. ABP 配置全局数据过滤器 II

    第一篇 那种写法有些复杂, 简单办法是直接注入 切换到 ***.EntityFramework 项目 在Uow 里面创建 ***EfUnitOfWork.cs 类 public class Coope ...

  2. 解决OneNote同步出错

    问题: onenote同步出现黄色叹号. 解决: 分析: 对每个分区进行设置密码,不能设置的证明该分区有问题.(可能不只一个分区卡同步) 解决方法: 1,将有问题的分区分制一份,然后删掉原来的分区 2 ...

  3. Gin + Vue全栈开发实战(二)

    尝试地写了第一篇自己学习Go Web框架的感受和入门的文章,发现反响还不错,大家也提出了很多的问题来一起交流.近期也渐渐地出现了很多有关go语言开发的相关文章,包括有在蚂蚁金服的大牛的分享,我也一直有 ...

  4. 消息中间件——RabbitMQ(三)理解RabbitMQ核心概念和AMQP协议!

    前言 本章学习,我们可以了解到以下知识点: 互联网大厂为什么选择RabbitMQ? RabbiMQ的高性能之道是如何做到的? 什么是AMQP高级协议? AMQP核心概念是什么? RabbitMQ整体架 ...

  5. Kali Linux-装机后通用配置

    目录 前言 一. 网络优化 更换host 更换dns 添加源 二. 更新系统 三 .安装N卡驱动 四.修复 add-apt-repository 五.安装常用软件 安装apt自带的包 安装第三方的de ...

  6. 《Java 8 in Action》Chapter 5:使用流

    流让你从外部迭代转向内部迭代,for循环显示迭代不用再写了,流内部管理对集合数据的迭代.这种处理数据的方式很有用,因为你让Stream API管理如何处理数据.这样Stream API就可以在背后进行 ...

  7. 玩转 SpringBoot 2 快速搭建 | RESTful Api 篇

    概述 RESTful 是一种架构风格,任何符合 RESTful 风格的架构,我们都可以称之为 RESTful 架构.我们常说的 RESTful Api 是符合 RESTful 原则和约束的 HTTP ...

  8. WTM重磅更新,LayuiAdmin and more

    从善如登,从恶如崩.对于一个开发人员来说,那就是做一个好的系统不容易,想搞砸一个系统很简单,删库跑路会还不会么. 对于我们开源框架的作者来说,做一个好的框架就像登山(也许是登天),我们一步一步往上走, ...

  9. 探索Asp net core3中的 项目文件、Program.cs和通用host(译)

    引言 原文地址 在这篇博客中我将探索一些关于Asp.net core 3.0应用的基础功能--.csproj 项目文件和Program源文件.我将会描述他们从asp.net core 2.X在默认模版 ...

  10. Unity进阶:PlayMaker

    版权申明: 本文原创首发于以下网站: 博客园『优梦创客』的空间:https://www.cnblogs.com/raymondking123 优梦创客的官方博客:https://91make.top ...