• Python全栈之路---day04(函数初识)


    函数存在的意义?

    之前我们都已经熟悉的使用过python自带的len()方法用来获取字符串的长度,但设想如果某一天这个方法突然不能用了,我们应该怎么办?以计算字符串hello world的长度为例,想实现这一需求并不麻烦:

    s = 'hello world'
    def my_len():
        i = 0
        for k in s:
            i += 1
        return i
    
    
    length = my_len()
    print(length)  # 11

    由此改功能得以实现,但是倘若我们想计算字符串“I love you”的长度,不得不修改S的值再进行类似的操作,虽然也能够达到目的,但总体感觉上来说总显得不够完美。为什么呢?

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

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

    于是,我们也想定义一个类似于len()的方法用来实现我们需要经常使用的操作,等到我们需要使用的时候直接调用函数名就能执行特定的代码一般。

    函数定义与调用

    先附上可以实现上述需求的一个示例函数的代码:

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

    函数定义:

         def 关键词开头,空格之后接函数名称和圆括号(),最后还有一个":"。

       def 是固定的,不能变,必须是连续的def三个字母,不能分开。。。它们要相亲相爱的在一起。

       空格 为了将def关键字和函数名分开,必须空(四声),当然你可以空2格、3格或者你想空多少都行,但正常人还是空1格。

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

       括号:是必须加的,先别问为啥要有括号,总之加上括号就对了!

    函数调用:

          就是 函数名()     ps:要记得加上括号

    注意:另外函数也有专属于他自己的注释风格

    # 函数的注释
    def func():
        '''
        这个函数实现了什么功能
        参数1:
        参数2:
        :return:是字符串或者列表的长度
        '''
        pass

    函数的返回值

      return关键字:该词翻译过来是“返回”的意思,故我们管其后的值为“返回值”。要想弄清楚返回值,我们得先知道返回值的几种情况:没有放回值、返回一个值、返回多个值

    没有返回值:

      不写return和只写return后面不写其他内容的情况下,函数都会返回一个None。你可能会因此产生疑问:既然没有要返回的值,为何嗨呀写一个return呢?这里要强调一下return的其他用法:即一旦你遇上return,就会结束整个函数,return后面的语句将不会再执行。

    返回一个值:

      这种情况只需在return后面加上要返回的值即可。

        注意:return可以返回任意数据类型的值,但是return和返回值之间要留有空格。

    返回多个值:

      return后面也可以返回任意多个、任意数据类型的值

    def ret_demo1():
        '''返回多个值''' 
        return 1,2,3,4
    
    
    def ret_demo2():
        '''返回多个任意类型的值'''
        return 1,['a','b'],3,4
    
    ret1 = ret_demo1()
    print(ret1)
    ret2 = ret_demo2()
    print(ret2)

      返回的多个值会被组织成元组被返回,也可以用多个值来接收

    def ret_demo2():
        return 1,['a','b'],3,4
    
    #返回多个值,用一个变量接收
    ret2 = ret_demo2()
    print(ret2)
    
    #返回多个值,用多个变量接收
    a,b,c,d = ret_demo2()
    print(a,b,c,d)
    
    #用多个值接收返回值:返回几个值,就用几个变量接收
    a,b,c,d = ret_demo2()
    print(a,b,c,d)

      PS:在python中,用逗号分割的多个值就被认为是一个元祖。

    函数的参数

       本篇开始就说到如果有需求想要计算另外字符串的长度,但又不想重复编写复杂的代码的时候,我们应该怎么做?其实只要定义一个带有参数的模板,将要计算的字符串赋给这个参数传入函数进行计算即可。这个过程就被称为传递参数,简称传参

    形参和实参

      传参的过程种又有实参和形参的区别:

      实参,全称为实际参数,简单的说就是我们想要进行处理的值,也是后续过程实际要交给函数的内容。

      形参,全称为形式参数,只是一个变量名,在定义函数的时候它只是一个形式,表示这里有一个参数。

    传递多个参数

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

      正因为多个参数的存在,才有这后续的一系列操作。。。

    位置参数

    站在实参角度

    1、按照位置传值

    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) #20

    2、按照关键字传值

    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) #20

    3、位置、关键字形式混合传值

    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)    #20

         注意:在位置和关键字两种形式混合使用的情况下,必须遵循“一个形参赋值一次,位置参数必须在关键字参数前面”的原则。

    默认参数

    由来:传参过程中㝉出现某些值重复出现的情况,此时将变化比较小的值设置成默认参数。

    def stu_info(name,sex = "male"):
        """打印学生信息函数,由于班中大部分学生都是男生,
            所以设置默认参数sex的默认值为'male'
        """
        print(name,sex)
    
    
    stu_info('凝宝')  #凝宝 male
    stu_info('柳柳','female') #柳柳 female

    参数陷阱:默认参数是一个可变数据类型

    #如果默认参数的值是一个可变数据类型,
    # 那么每一次调用函数的时候,如果不传值就共用这个数据类型的资源
    def qqxing(l=[]):
        l.append(1)
        print(l)
    
    qqxing()#[1]
    qqxing([])#[1]
    qqxing()#[1, 1]
    qqxing()#[1, 1, 1]

    动态参数

    可以接受任意多个参数
    两种:
    *args:接收的是按照位置传参的值,组织成一个元组
    **kwargs:接收的是按照关键字传参的值,组织成一个字典
    args必须在kwargs之前
    def sum(*args):
        n = 0
        for i in args:
            n += i
        return n
    
    
    print(sum(1, 2))  # 3
    print(sum(1, 2, 3))  # 6
    print(sum(1, 2, 3, 4))  # 10
    def func(**kwargs):
        print(kwargs)
    
    
    func(a=1, b=2, c=3)  # {'a': 1, 'b': 2, 'c': 3}
    func(a=1, b=2)  # {'a': 1, 'b': 2}
    func(a=1)  # {'a': 1}
    def func1(*args, **kwargs):
        print(args, kwargs)
    
    
    func1(1, 2, 3, 4, 5, a='adsd', b='cda')  # (1, 2, 3, 4, 5) {'a': 'adsd', 'b': 'cda'}
    总顺序:位置参数,*args,默认参数,**kwargs

    命名空间和作用域

    命名空间

    命名空间的本质:存放名字与值的绑定关系

    命名空间一共分为三种:

    内置命名空间--python解释器

       就是python解释器一启动就可以使用的名字存储在内置明明空间中
    内置的名字在启动解释器的时候加载进内存

    全局命名空间--我们写的代码但不是函数中的代码

       程序在执行过程中依次加载进内存
    放置了我们设置的所有变量名和函数名

    局部命名空间--函数

       就是函数内部定义的名字
    当调用函数的时候才会产生这个名称空间,随着函数执行的结束,这个明明空间就又消失了

    三种命名空间之间的加载与取值顺序:

    加载顺序:内置命名空间(程序运行前加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载)
    取值:在局部调用:局部命名空间->全局命名空间->内置命名空间

       在全局调用:全局命名空间->内置命名空间
    #在局部:可以使用全局、内置命名空间中的名字
    #在全局:可以使用内置命名空间的名字,但是不能使用局部的
    #在内置:不能使用局部和全局的名字的
    #范围:内置>全局>局部(小范围可以使用大范围)

    #在正常情况下,直接使用内置的名字
    #当我们在全局中定义了与内置同名的名字时会使用全局的名字
    #多个函数应该拥有多个独立的局部名字空间,不互相共享 

    作用域

    全局作用域-->作用在全局:内置和全局名字空间中的名字都属于全局作用域
    局部作用域-->作用在局部:函数(局部命名空间中的名字都属于局部作用域) locals()查看

    对于不可变数据类型:在局部可以查看全局作用域中的变量,但是不能直接修改。如果想要修改,需要在程序的一开始添加global声明
    如果在一个局部(函数)内声明了一个global变量,那么这个变量在局部的所有操作将对全局的变量有效
    a=1
    def fun():
        global a
        a+=1
    
    fun()
    print(a)    #2
    a=1
    b=2
    def fun():
        x='aaa'
        y='bbb'
        print(locals())#{'x': 'aaa', 'y': 'bbb'}
    fun()
    print(globals())#{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000016EB4011CC0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'E:/Python/day07/函数进阶.py', '__cached__': None, 'func1': <function func1 at 0x0000016EB531A9D8>, 'a': 1, 'b': 2, 'fun': <function fun at 0x0000016EB531AA60>}
    print(locals())#本地的
    #globals永远打印全局的名字
    #locals 输出什么 根据locals所在的位置
     函数的嵌套和作用域链
     函数的嵌套调用
    def max(a,b):
        return a if a>b else b
    
    def my_max(x,y,z):
        c=max(x,y)
        return max(c,z)
    
    print(my_max(1,2,3))    #3
    函数的嵌套定义
    def f1():
        print("in f1")
        def f2():
            print("in f2")
    
        f2()
    f1()
    函数的嵌套定义(一)

    def f1():
        def f2():
            def f3():
                print("in f3")
            print("in f2")
            f3()
        print("in f1")
        f2()
        
    f1()
    函数的嵌套定义(二)

     

      函数的作用域链

    def f1():
        a = 1
        def f2():
            print(a)
        f2()
    
    f1()
    作用域链(一)

     

    def f1():
        a = 1
        def f2():
            def f3():
                print(a)
            f3()
        f2()
    
    f1()
    作用域链(二)
    def f1():
        a = 1
        def f2():
            a = 2
        f2()
        print('a in f1 : ',a)
    
    f1()
    作用域链(三)

     

    nonlocal:只能用于局部变量,找上一层中离当前函数最近一层的局部变量  -->对全局无效,对局部也只是对最近的一层有影响
    声明了nonlocal的内部函数的变量修改会影响到离当前函数最近一层的局部变量
    a=1
    def outer():
        a=1
        def inner():
            a=2
            print(a)
            print("inner")
            def inner2():
                nonlocal a #声明了一个上面第一层局部变量
                a+=1
                print('inner2')
            inner2()
            print("##a##",a)
        inner()
        print("**a**:",a)
    outer()
    print("**a**:",a)

    函数名的本质

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

    1、可以被引用

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

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

    def f1():
        print('f1')
    
    
    def f2():
        print('f2')
    
    
    def f3():
        print('f3')
    
    l = [f1,f2,f3]
    d = {'f1':f1,'f2':f2,'f3':f3}
    #调用
    l[0]()
    d['f2']()

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

    def func():
        print(123)
    
    def wahaha(f):
        f()
        return f        #函数名可以作为函数的返回值
    
    qqxing=wahaha(func)    #函数名可以作为函数的参数   123
    qqxing()

     

    闭包

    闭包函数:

    内部函数包含对外部作用域而非全剧作用域名字的引用,该内部函数称为闭包函数
    #函数内部定义的函数称为内部函数

    def outer():
        a=1
        def inner():
            print(a)
        print(inner.__closure__) #(<cell at 0x0000029E98BC7678: int object at 0x00007FFADB639340>,)
    outer()

     如上述代码中,inner函数即为闭包函数,可用__closure__方法进行判断

    闭包函数常用方法

    def outer():
        a=1
        def inner():
            print(a)
        return inner
    inn=outer()
    inn()  

    闭包的嵌套:

    def wrapper():
        money = 1000
        def func():
            name = '凝宝'
            def inner():
                print(name,money)
            return inner
        return func
    
    f = wrapper()
    i = f()
    i()

     

  • 相关阅读:
    java 学习 —— AWT布局组件
    Java 学习 ——网络与通信
    Java 学习————线程直接通信
    Java 学习————多线程同步
    改善PHP开发方式的5种方法
    grep在linux操作系统php,pytho等开发中的应用
    CSS border边框属性教程(color style)
    css输入框文字点击消失输入文字颜色变深JQ特效
    DIVCSS5模块 上标题下简介列表DIV CSS布局
    DIV CSS position绝对定位absolute relative教程篇
  • 原文地址:https://www.cnblogs.com/zhuoning/p/11368084.html
Copyright © 2020-2023  润新知