• 函数基础


    # 1、为什么要用函数?
    # 2、函数的定义与调用
    # 3、函数的返回值
    # 4、函数的参数
    # 5、命名空间和作用域
    # 6、函数嵌套及作用域链
    # 7、函数名的本质
    # 8、函数的闭包
    导读目录

    1、为什么要用函数?

      假如len方法突然不能直接用了,现在有个需求是【计算'hello world'的长度】:

    s1 = "hello world"
    length = 0
    for i in s1:
        length = length+1
    
    print(length)

      现在又新增了一个需求,要计算另外一个字符串的长度,"hello China"。于是,这个时候你的代码就变成了这样:

    s1 = "hello world"
    length = 0
    for i in s1:
        length = length+1
    print(length)
    
    s2 = "hello China"
    length = 0
    for i in s2:
        length = length+1
    print(length)

      这会造成:

      首先,之前只要执行len方法就可以直接拿到一个字符串的长度了,现在为了实现相同的功能我们把相同的代码写了好多遍 —— 代码冗余

      其次,之前我们只写两句话读起来也很简单,一看就知道这两句代码是在计算长度,但是刚刚的代码却不那么容易读懂 —— 可读性差

      于是乎,要是能像len一样将这段代码起个名字就好了,需要的时候直接“喊名字”就可以了。

    2、函数的定义与调用

      把上面求字符串长度的代码封装起来:

    #函数定义
    def mylen():
        """计算s1的长度"""
        s1 = "hello world"
        length = 0
        for i in s1:
            length = length+1
        print(length)
    
    #函数调用  
    mylen()

      函数名:函数名只能包含字符串、下划线和数字,且不能以数字开头。虽然函数名可以随便起,但我们给函数起名字还是要尽量简短,并能表达函数功能。

    3、函数的返回值

      将上面函数定义里的print改成return,在函数调用的时候看需求再print

    #函数定义
    def mylen():
        """计算s1的长度"""
        s1 = "hello world"
        length = 0
        for i in s1:
            length = length+1
        return length
    
    #函数调用
    str_len = mylen()
    print('str_len : %s'%str_len)

      3.1 return关键字的作用

      划重点:一旦遇到return,就结束整个函数

      返回值有几种情况:分别是没有返回值、返回一个值、返回多个值。

        3.1.1 没有返回值

    # return   <=>   return None   <=>   没有return
    
    def ret_demo():
        print(111)
        return
        print(222)
    
    ret = ret_demo()
    print(ret)
    ------------------------------------------------------------------------------------
    def ret_demo():
        print(111)
        return None
        print(222)
    
    ret = ret_demo()
    print(ret)
    ------------------------------------------------------------------------------------
    def ret_demo():
        print(111)
        print(222)
    
    ret = ret_demo()
    print(ret)

        3.1.2 返回一个值

    # 注意:return和返回值之间要有空格,可以返回任意数据类型的值
    def ret_demo():
        return 111
        print(222)
    
    ret = ret_demo()
    print(ret)    # 111

        3.1.3 返回多个值

    # 可以返回任意多个、任意数据类型的值
    def ret_demo1():
        '''返回多个值'''
        return 1,2,3,4
    
    def ret_demo2():
        '''返回多个任意类型的值'''
        return 1,['a','b'],3,4
    
    ret1 = ret_demo1()
    print(ret1)                # (1, 2, 3, 4)
    ret2 = ret_demo2()  # (1, ['a', 'b'], 3, 4)
    print(ret2)
    def ret_demo2():
        return 1,['a','b'],3,4
    
    #返回多个值,用一个变量接收
    ret2 = ret_demo2()
    print(ret2)       # (1, ['a', 'b'], 3, 4)
    
    #返回几个值,就用几个变量接收
    a,b,c,d = ret_demo2()
    print(a,"->",b,"->",c,"->",d)   # 1 -> ['a', 'b'] -> 3 -> 4
    #序列解压一
    >>> a,b,c,d = (1,2,3,4)
    >>> a
    1
    >>> b
    2
    >>> c
    3
    >>> d
    4
    #序列解压二
    >>> a,_,_,d=(1,2,3,4)
    >>> a
    1
    >>> d
    4
    >>> a,*_=(1,2,3,4)
    >>> *_,d=(1,2,3,4)
    >>> a
    1
    >>> d
    4
    #也适用于字符串、列表、字典、集合
    >>> a,b = {'name':'eva','age':18} 
    >>> a
    'name'
    >>> b
    'age'
    序列解压扩展

    4、函数的参数

    #函数定义
    def mylen(s1):
        """计算s1的长度"""
        length = 0
        for i in s1:
            length = length+1
        return length
    
    #函数调用
    str_len = mylen("hello world")
    print('str_len : %s'%str_len)

      4.1 实参和形参

      我们调用函数时传递的这个“hello world”被称为实际参数,因为这个是实际的要交给函数的内容,简称实参。

      定义函数时的s1,只是一个变量的名字,被称为形式参数,因为在定义函数的时候它只是一个形式,表示这里有一个参数,简称形参

      4.2 传递多个参数

      参数可以传递多个,多个参数之间用逗号分割。

    def mymax(x,y):
        the_max = x if x > y else y
        return the_max
    
    ma = mymax(10,20)
    print(ma)

      4.3 位置参数

        4.3.1 站在实参角度:(顺序:位置参数,默认参数,关键字参数a = '')

          对于一个形参只能赋值一次

    # ①按位置传参
    def mymax(x,y):
        #此时x=10,y=20
        the_max = x if x > y else y
        return the_max
    
    ma = mymax(10,20)
    print(ma)
    # ② 按照关键字传参
    def mymax(x,y):
        #此时x = 20,y = 10
        print(x,y)
        the_max = x if x > y else y
        return the_max
    
    ma = mymax(y = 10,x = 20)
    print(ma)
    # ③ 位置、关键字形式混着用
    def mymax(x,y):
        #此时x = 10,y = 20
        print(x,y)
        the_max = x if x > y else y
        return the_max
    
    ma = mymax(10,y = 20)
    print(ma)

        4.3.2 站在形参角度:(顺序:*args,默认参数,**kwargs)

    def func(*args,default = 1,**kwargs):      #形参顺序: (*args,默认参数,**kwargs)
        print(args,default,kwargs)
    func(1,2,3,default=2,a='aaaa',b='bbbbb')   #实参顺序: (位置参数,默认参数,关键字参数a = '')
    
    运行结果:
    (1, 2, 3) 2 {'a': 'aaaa', 'b': 'bbbbb'}   

        位置参数必须传值

    def mymax(x,y):
        print(x,y)
        the_max = x if x > y else y
        return the_max
    
    #调用mymax不传递参数
    ma = mymax()
    print(ma)
    
    #结果
    TypeError: mymax() missing 2 required positional arguments: 'x' and 'y'

      4.4 默认参数

    # 默认参数的值是一个可变数据类型
    # 每一次调用函数的时候,如果不传值,就公用这个数据类型的资源
    
    def stu_info(name,sex = "male"):
        """打印学生信息函数,由于班中大部分学生都是男生,
            所以设置默认参数sex的默认值为'male'
        """
        print(name,sex)
    
     
    stu_info('alex')               # alex male
    stu_info('eva','female')   # eva female
    # 默认参数的陷阱
    def qqxing(k,d = {}):
        d[k] = 'v'
        print(d)
    
    qqxing(1)    #{1: 'v'}
    qqxing(2)    #{1: 'v', 2: 'v'}    #新执行完的都添加到默认的dict里边
    qqxing(3)    #{1: 'v', 2: 'v', 3: 'v'}

      4.5 动态参数

    # *args:按位置传值多余的参数都由args统一接收,保存成一个元组的形式
    def mysum(*args):
        the_sum = 0
        for i in args:
            the_sum+=i
        return the_sum
    
    the_sum = mysum(1,2,3,4)
    print(the_sum)
    # **kwargs
    def stu_info(**kwargs):
        print(kwargs)
        print(kwargs['name'],kwargs['sex'])
    
    stu_info(name = 'alex',sex = 'male')

      参数总结

    5、命名空间和作用域

      5.1 命名空间

      【依赖倒置原则】:向上依赖
      【向下选择原则】:当我自己有的时候,我就不找我的上级要了;如果我自己没有,就一层一层往上级找(找级数最低的),如果内置的命名空间里也没有,则报错
       多个函数应该拥有多个独立的局部名字空间,不互相共享

    命名空间
    #
    # 内置命名空间 -- python解释器 # python解释器一启动就可以使用的名字存储在内置命名空间中 # 内置的名字在其冻结时期的时候被加载进内存里 ## 全局命名空间 -- 我们写的代码,但不是函数中的代码 #是在程序从上到下被执行的过程中依次加载进内存的 #放置了我们设置的所有变量名和函数名 ## 局部命名空间 -- 函数 #就是函数内部调用的名字 #当调用函数的时候,才会产生这个名称空间,随着函数执行的结束,这个命名空间就又消失了 -------------------------------------------------------------------------------- ----##三种命名空间之间的加载与取值顺序: #加载顺序: #内置命名空间(程序运行前加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载) # 取值顺序: # 在局部调用:局部命名空间->全局命名空间->内置命名空间         x = 1         def f(x):         x=3         print(x)         f(2) # 3         print(10) # 10 # 在全局调用:全局命名空间->内置命名空间         x = 10         def f(x):         print(x)         f(2) # 2         print(x) # 10

      5.2 作用域

      作用域就是作用范围,按照生效范围可以分为全局作用域和局部作用域。

      5.2.1 globals()  和 locals()方法

      【全局作用域】: -- globals():永远打印全局的名字

          包含内置名称空间、全局名称空间,在整个文件的任意位置都能被引用、全局有效。

      【局部作用域】:-- locals():取决于位置,在哪儿哪儿就是本地

          局部名称空间,只能在局部范围生效  。

    a = 1
    b = 2
    def func():
        x = 'aaa'
        y = 'bbb'
        print(locals())
        print(globals())  #全局的:永远打印全局的名字
    func()
    
    运行结果:
    {'y': 'bbb', 'x': 'aaa'}
    
    {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00000219469AEF28>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'F:/Python/Project/All_Stacks/01 基础/day10/02.函数的命名空间.py', '__cached__': None, 'a': 1, 'b': 2, 'func': <function func at 0x00000219468A3E18>}

      5.2.2 global关键字

    a = 10
    def func():
        global a     # 在函数(局部命名空间)中执行的是全局的变量a
        print('func中a的值:',a)
        a = 20       # 修改的是全局的a的值,生效
    print('全局中a的值:',a)
    func()
    print('执行完func后全局中a的值:',a)
    
    运行结果:
    全局中a的值: 10
    func中a的值: 10
    执行完func后全局中a的值: 20

    6、函数嵌套及作用域链 

      6.1 函数嵌套

        内部函数使用外部函数的变量

    def max2(x,y):
        m  = x if x>y else y
        return m
    
    def max4(a,b,c,d):
        res1 = max2(a,b)
        res2 = max2(res1,c)
        res3 = max2(res2,d)
        return res3
    
    ret = max4(23,-7,31,11)
    print(ret)     # 31
    
    --------------------------------------------------------------------------------
    
    def f1():
        def f2():
            def f3():
                print("in f3") #
            print("in f2")    #
            f3()                 #
        print("in f1")        #
        f2()                     #
    f1()
    
    运行结果:
    in f1
    in f2
    in f3
                                                        

      6.2 函数作用域链

    def f1():
        a = 1
        def f2():
            def f3():
                print(a)
            f3()
        f2()
    f1()      # 1
    
    ------------------------------------------------------------------------------------
    
    def f1():
        a = 3
        def f2():
            a = 2
        f2()       # 并没有改变 f1函数中a的值
        print('a in f1 : ',a)
    f1()    # 3

      6.3 nonlocal关键字

    #定义:
      # 声明了一个上层的局部变量,作用域只到 上层第一个 有该变量(最低层),找上层中离当前函数最近一层的局部变量
         # 声明了nonlocal的内部函数变量修改,会影响到当前函数最近一层的局部变量
    #规则:
        # 1.外部必须有这个变量
        # 2.在内部函数声明nonlocal变量之前不能再出现同名变量
        # 3.内部修改这个变量如果想在外部有这个变量的第一层函数中生效
    
    def f1():
        a = 1
        def f2():
            nonlocal a
            a = 2
        f2()
        print('a in f1 : ',a)     # a in f1 :  2
    
    f1()

      6.4 综合示例

    a = 1
    def outer():
        a = 1       #此处是局部变量
        def inner():
            a = 3
            b = 2
            print("inner中a的值:",a)
            print('inner')   #此处只是定义,并没有调用
            def inner2():
                # global a     #声明了一个全局变量,修改的是全局的a,不修改局部的
                nonlocal a     #声明了一个上层的局部变量,作用域只到 上层第一个 有该变量(最低层),找上层中离当前函数最近一层的局部变量
                               # 声明了nonlocal的内部函数变量修改,会影响到当前函数最近一层的局部变量
                a += 1       #不可变数据类型的修改
                print('inner2中的a:%d'%a)
                # print('inner2')
            inner2()
        inner()
        print('局部的a:%d'%a)
    outer()
    print('全局a:%d'%a)
    
    运行结果:
    inner中a的值: 3
    inner
    inner2中的a:4
    局部的a:1
    全局a:1

    7、函数名的本质

       函数名本质上:就是函数的内存地址

    def func():
        print(123)
    func2 = func         ###  函数名可以赋值
    func2()    # 123

      7.1 可以被引用

    def func():
        print('in func')
    
    f = func
    print(f)       #<function func at 0x000001C6CF353E18>

      7.2 可以被当做容器类型的元素

    def func():
        print(123)
    func2 = func         # 函数名就是内存地址,可以赋值
    l = [func,func2]     # 函数名可以作为容器类型(如list,dict等)的元素
    print(l)
    for i in l:     # i <=> 函数名
        i()         # 调用函数
    
    运行结果:
    [<function func at 0x0000025136343E18>, <function func at 0x0000025136343E18>]
    123
    123

      7.3 可以当做函数的参数和返回值

    def func():
        print(123)
    def wahaha(f):
        f()                 # 执行func()
        return f            # 函数名可以作为函数的返回值
    qqxing = wahaha(func)   # 函数名可以作为函数的参数   <=> qqxing = f
    qqxing()                # 等价于执行  func()
    
    运行结果:
    123
    123

    8、函数的闭包

       闭包发生在嵌套函数中:

    # bar函数不需要传参数,就能获取到外面的变量,这是一个典型的闭包现象。
    
    def foo():
        name = "Andy"  # 定义了一个foo函数内部的局部变量
        def bar():
            print(name)  # 在bar函数内部引用了其外部函数foo的局部变量name
        return bar
    
    func = foo()    # 相当于执行 func = bar
    func()          # 相当于执行 bar()     # Andy

      8.1 闭包的定义

      函数内部定义的函数,称为内部函数。这个内部函数只有使用了它外部函数的局部变量,即使外部函数返回了,这个内部函数还可以访问到外部函数的局部变量,这种现象就叫做闭包

      8.2 闭包的本质

      闭包:是由函数和与它相关的引用环境组合而成的实体。

      像下面的 func函数就不能称为闭包,因为它并没有使用包含它的外部函数的局部变量。

    name = "Alex"  # 定义了一个全局变量
    def foo():
        def bar():
            print(name)  # 在bar函数内部引用了全局变量name
        return bar
    
    func = foo()
    print(func.__closure__)      # None

      8.3 判断闭包函数的方法__closure__

    #输出的__closure__有cell元素 :是闭包函数
    def func():
        name = 'eva'
        def inner():
            print(name)
        print("inner.__closure__ : ",inner.__closure__)
        return inner
    f = func()
    f()    
    
    #运行结果:
    (<cell at 0x00000215E4550C48: str object at 0x00000215E46F84C8>,)
    eva
    
    ————————————————————————————————————
    
    #输出的__closure__为None :不是闭包函数
    name = 'egon'
    def func2():
        def inner():
            print(name)
        print("inner.__closure__ : ", inner.__closure__)
        return inner
    f2 = func2()
    f2()
    
    #运行结果:
    None
    egon

       8.4 闭包嵌套

    def wrapper():
        money = 1000
        def func():
            name = 'eva'
            def inner():
                print(name,money)   # eva 1000
            return inner
        return func
    f = wrapper()
    i = f()
    i()
  • 相关阅读:
    zookeeper常用命令
    linux查看日志相关命令
    第三十一期: VueRouter源码浅析 传统前端和多媒体前端
    第二十八期:模型,模块,组件,框架和架构
    星际2光速注卵
    星际心得
    英语词根研究和单词记忆
    星际2如何离线模式打电脑和rpg地图练操作
    星际研究
    第一篇帖子:不利用中间变量交换两个数字的小算法题
  • 原文地址:https://www.cnblogs.com/timetellu/p/10681272.html
Copyright © 2020-2023  润新知