zoukankan      html  css  js  c++  java
  • [TimLinux] Python 函数(2)

    1. 作用

    • 最大化的代码重用:建设复制、粘贴
    • 最小化的代码冗余:减少重复代码
    • 流程分解:将做一件事情分解为相应的步骤,不同步骤封装在不同的函数中。

    2. 定义

    def 函数名(可选的参数列表):
        函数体

    函数名:命名需要符合Python标识符规范:开头不能为数字,只能包含字母、数字、下划线。

    可选的参数列表:

    • 一个参数都不需要,括号内什么都不填
    • 位置参数:传递一个或多个参数,依次排列下去
    • 关键字参数:通过使用 “名=值” 的方式来传递参数
    • 变长的位置参数:*args,在一个变量名前面加上"*"号,可以将args列表中的每一项作为一个参数进行传递
    • 变长的关键字参数:**kwargs,在一个变量名前面加上" ** ",可以将kwargs字典中的每一项作为一个参数进行传递
    • 关键字参数必须在位置参数之后进行传递。

    函数体:

    • 空函数体,使用 pass 语句进行设置
    • 其他内容按照正常逻辑编写Python代码,构成函数体。

    2.1. 设计的概念

    1. def : def 是语句中的一种,按照语句固有的格式来定义函数,函数并不存在,直到运行了def语句后,才存在,def语句可以嵌套在其他如:if, while, def等语句中。
    
    2. def 创建了一个对象,并将对象赋值给一个变量名,执行def语句将生成一个函数对象,并最终赋值给了函数名,函数名是对某一函数的引用,函数名可以像其他变量名一样使用,
    比如存入列表等。
    3. lambda 创建了一个对象,但将其作为结果返回,lambda属于表达式,用于一条def语句不能工作的场景中。 4. return 将一个结果对象发送给调用者,将计算值返回给调用者。 5. yield 向调用者发回一个结果对象,但是记住它离开的地方,生成器函数的返回值是有yield语句返回的,每次返回后将挂起,并在下次调用时恢复。 6. global 声明了一个模块级的变量并被赋值。默认情况所有在变量中声明的变量只属于这个函数,函数的本地变量,生命周期是函数被调用时,global声明的变量来自模块最顶层。 7. nonlocal 声明了将要赋值的一个封闭的函数变量,变量冻结在函数作用域内,每次调用将继承上次变量最后的状态。 8. 函数是通过赋值(对象引用)传递的,参数是通过引用传递的,改变参数名不会改变实参的值,但是当传递的是一个可变对象(列表、字典等)则会导致共同使用的一片内存区域发生修改。 9. 参数、返回值以及变量并不是声明:Python没有类型约束,参数类型不受限制。

     2.2. 多态

    代码并不关心特定的数据类型,通过测试代码来替换编译器规定不同的数据类型,不需要:实现相同的功能,为不同的数据类型参数,编写多个函数。

    2.3. 本地变量

    所有本地变量都会在函数调用是出现,并在函数退出时消失。

    3. 作用域

    将一个变量名被赋值的地点(绑定给)关联为一个特定的命名空间。在代码中,给一个变量赋值的地方,决定了这个变量可见的范围。

    • 模块存在一级作用域
    • 类存在一级作用域
    • 函数存在一级作用域
    • 向前搜索,不向后搜索(比如JavaScript,它只存在函数级作用域,变量是可以认为会向后搜索的)

     变量的作用域,只根变量被赋值的地方有关,与函数的调用等是无关的。

    3.1. 作用域法则

    嵌套的命名空间形成了作用域的规则。

    • 函数定义了本地作用域
    • 模块定义了全局作用域

    两个作用域间的关系:

    • 内嵌的模块是全局作用域:每个模块都是一个全局作用域。
    • 全局作用域的作用范围仅限于单个文件:“全局”指的是一个文件顶层变量仅对这个文件内部代码而言是“全局”。
    • 每次对函数的调用都创建了一个新的本地作用域。
    • 赋值的变量名除非声明为全局变量或非本地变量,否则均为本地变量:默认函数内变量为本地作用域内,通过global、nonlocal间变量引用到全局变量、嵌套函数间变量。
    • 所有其他的变量名都可以归纳为本地、全局、内置三种作用域。

     3.2. 变量名解析机制(LEGB原则)

    • L: Local,局部作用域
    • E: Enclosing 直接外围作用域
    • G: Global 全局作用域
    • B: Builtin 内建作用域

    变量四个查找步骤:1. 本地作用域,2. 函数内, 3. 全局, 4. 最后内置。任一步骤找到变量名立即停止,查找到最后一步都未找到报错。

     3.3. 示例

    x = 99
    def f1(y):
        z = x + y  # x 来自global,但是不需要global关键字
        return z
    print(f1(2))
    
    # 输出:101
    x = 99
    def f1(y):
        print("f1: x + y = ", x+y)
        def f2(z):
            print("f2: x + z = ", x+z)
        f2(2)
    f1(1)
    
    # output:
    # f1: x + y =  100
    # f2: x + z =  101
    # x 变量从全局移到函数内部,f1(1)在全局调用,f2(2)在f1(1)内部调用,工作正常。
    
    # x = 99
    def f1(y):
        x = 88
        print("f1: x + y = ", x+y)
        def f2(z):
            print("f2: x + z = ", x+z)
        f2(2)
    f1(1)
    
    # output:
    # f1: x + y =  89
    # f2: x + z =  90
    
    # 这个例子也能够正常工作,是因为变量的查找顺序是匹配LEGB原则的。
    # x = 99
    def f1(y):
        x = 88
        print("f1: x + y = ", x+y)
        def f2(z):
            print("f2: x + z = ", x+z)
        return f2
    new_f2 = f1(1)
    new_f2(2)
    
    # output:
    # f1: x + y =  89
    # f2: x + z =  90
    
    # 而下面这段代码不能够工作,是因为x+=1是一条赋值语句,x属于E级作用域,在L级
    # 作用域内赋值,跨作用域修改变量无法成功。
    x = 99
    def f1(y):
        x = 88  # 这句成功了,是因为直接赋值,创建了一个L级作用域变量
        print("f1: x + y = ", x+y)
        def f2(z):
            x += 1  # 代码等价 x = x + 1,第二个位置的x会在本地变量中查找,这是一个循环引用,导致提示以下错误。如果改为 w = x + 1,则不存在这样的问题。
            print("f2: x + z = ", x+z)
        return f2
    new_f2 = f1(1)
    new_f2(2)
    
    # output:
    # Traceback (most recent call last):
    # f1: x + y =  89
    #   File "D:/作用域.py", line 10, in <module>
    #     new_f2(2)
    #   File "D:/作用域.py", line 6, in f2
    #     x = x + 1
    # UnboundLocalError: local variable 'x' referenced before assignment

     3.4. global、nonlocal

    • global:允许修改一个模块文件的顶层变量名(定义在def语句之外的变量名)。对变量修改,之后在访问变量,变量确实被修改了。
    • nonlocal:允许修改一个def语句内最近一层变量名(比如多层嵌套def,查找nonlocal声明的变量,是查找最靠近nonlocal声明语句的位置)。对变量修改,之后在访问变量,变量也确实被修改了。

     3.5. 闭包/工厂函数

    一种记住临时对象的方法,当一个变量离开它的作用域,按照正常逻辑,变量应该被消亡,但是使用一种技术可以将这样的临时变量保存下,这就是闭包。

    使用了闭包技术,能够技术这样的变量的函数,称为闭包函数,或者工厂函数。示例

    def maker(n):
        print("maker: n", n)
        def tmp(x):
            x *= n
            print(x)
        return tmp
    f = maker(2)
    f(2)
    g = maker(3)
    g(2)
    
    # output:
    # maker: n 2
    # 4
    # maker: n 3
    # 6

    如果要对外部函数的变量进行修改,这时就需要使用nonlocal,使用将外部变量赋值新变量是无法每次调用记录上次值的功能的。

    4. 函数参数

    关键点:

    • 参数的传递是通过自动将对象赋值给本地变量名来实现:

    5. 高级话题

    TBD

    6. 迭代和解析

    TBD.

  • 相关阅读:
    软件设计师考试知识点总结
    HTML和CSS
    JavaScript核心知识点
    操作系统--页面置换算法(缺页数计算)
    中标麒麟系统远程桌面连接
    数据结构 图
    数据结构 二叉树
    MATLAB 大数据剔除坏值
    PTA 邻接表存储图的广度优先遍历(20 分)
    PTA 邻接矩阵存储图的深度优先遍历
  • 原文地址:https://www.cnblogs.com/timlinux/p/9195669.html
Copyright © 2011-2022 走看看