美文网首页视觉艺术
Python函数 | 函数详解

Python函数 | 函数详解

作者: 生信师姐 | 来源:发表于2020-03-30 17:02 被阅读0次

    一、函数初识

    函数的产生:函数就是封装一个功能的代码片段。

    li = ['spring', 'summer', 'autumn', 'winter']
    def function():
        count = 0
        for j in li:
            count += 1
        print(count)
    function()        # 4
    
    • def 关键字,定义一个函数
    • function 函数名的书写规则与变量一样。
    • 括号是用来传参的。
    • 函数体,就是函数里面的逻辑代码

    代码从上至下执行,执行到def function() 时, 将function这个变量名加载到临时内存中,但它不执行

    函数的执行:函数名 + ()

    使用****name****方法获取函数名 ,******使用****doc_****方法获取**函数的解释 ****

    def func1():
        """
        此函数是完成登陆的功能,参数分别是...作用。
        return: 返回值是登陆成功与否(True,False)
        """
        print(666)
    
    func1()
    print(func1.__name__)         #获取函数名
    print(func1.__doc__)         #获取函数名注释说明 
    
    执行输出:
    666 func1
    此函数是完成登陆的功能,参数分别是...作用。
    return: 返回值是登陆成功与否(True,False)</pre>
    

    这个有什么用呢?比如日志功能,需要打印出谁在什么时间,调用了什么函数,函数是干啥的,花费了多次时间,这个时候,就需要获取函数的有用信息了

    1. 函数返回值

    写函数,不要在函数中写print()****, ****函数是以功能为导向的,除非测试的时候,才可以写print()

    • 在函数中,遇到return结束函数
    def fun():
        print(111)
        return
        print(444)
    fun() 
    
    执行输出:111
    
    • 将值返回给函数的调用者
    def fun():
        a = 134
        return a
    print(fun()) 
    
    执行输出:123
    

    1)无 return

    def fun():
        pass
    print(fun()) 
    
    执行输出:**None**
    

    2)return 1个值。该值是什么,就直接返回给函数的调用者,函数名()

    def fun():
        return [1,2,3]
    print(fun()) 
    执行输出:[1, 2, 3]
    

    3)return 多个值 将多个值放到一个元组里,返回给函数的调用者。

    def fun():
        return 1,2,[33,44],'abc'
    print(fun()) 
    
    执行输出:
    (1, 2, [33, 44], 'abc')
    

    2. 函数的传参

    (1)实参:在函数执行者里面的参数叫实参
    位置参数:按顺序****,****一一对应

    def func(a,b,c):
        print(a)
        print(b)
        print(c)
    
    func('fdsafdas',3,4) 
    
    执行输出:
    fdsafdas
    3
    4
    

    如果少一个参数呢?

    def func(a,b,c):
        print(a)
        print(b)
        print(c)
    
    func(3,4) 
    
    执行报错:TypeError: func() missing 1 required positional argument: 'c'
    

    必须是一一对应的。

    def compare(x,y):
        ret = x if x > y else y   #三元运算,针对简单的if else才能使用
        return ret
    
    print(compare(123,122334))        # 122334
    

    关键字参数:可以不按顺序,但是必须一一对应

    def compare(x,y):
        ret = x if x > y else y
        return ret
    print(compare(y=13,x=1))
    
    执行结果:13
    

    混合参数:关键字参数一定要在位置参数后面

    def func1(a,b,c,d,e):
        print(a)
        print(b)
        print(c)
        print(d)
        print(e)
    
    func1(1,4,d=2,c=3,e=5) 
    
    执行输出:
    1
    4
    3
    2
    5
    

    (2) 形参:
    位置参数:按顺序和实参一一对应,位置参数必须传值

    def func(a,b,c):
        print(a)
        print(b)
    print(c)
    
    func('fdsafdas',3,4) 
    
    执行输出:
    fdsafdas
    3
    4
    

    默认参数:传参则覆盖,不传则默认,默认参数永远在位置参数后面
    例1.

    def func(a,b=666):
        print(a,b)
    func(1,2) 
    
    执行输出:1 2
    

    例2.

    def func(a,b=666):
        print(a,b)
    func(1) 
    
    执行输出:1 666
    

    举一个场景:班主任录入员工信息表,有2个问题:第一,男生居多;第二,完成函数功能** *******

    def  Infor(username,sex='男'):
        with open('name_list',encoding='utf-8',mode='a') as f1:
            f1.write('{}\t{}\n'.format(username,sex))
    
    while True:
        username = input('请输入姓名(男生以1开头):').strip()
        if '1' in username:
            username = username[1:]        #去除1
            Infor(username)
        else:
            Infor(username,'女') 
    

    ③动态参数当函数的形参数量不一定时,可以使用动态参数。用args和kwargs接收,args是元组类型,接收除键值对以外的参数(接收位置参数),kwargs是字典类型,接收键值对(关键字参数)****,****并保存在字典中。*

    def func(*args,**kwargs):
        print(args,type(args))
        print(kwargs,type(kwargs))
    
    func(1,2,3,4,'alex',name = 'alex')
    
    输出结果是:
    (1, 2, 3, 4, 'alex') <class 'tuple'> {'name': 'alex'} <class 'dict'>
    

    “ * "的魔性作用

    (1)在函数定义时:位置参数和关键字参数代表聚合*

    将所有实参的位置参数聚合到一个元组中,并将这个元组赋值给args。在关键参数前加“ ** ”代表将实参的关键字参数聚合到一个字典中,并将这个字典赋值给kwargs。

    将2个列表的所有元素赋值给args

    def func(*args):
        print(args)
    
    l1 = [1,2,30]
    l2 = [1,2,33,21,45,66]
    func(*l1)
    func(*l1,*l2) 
    
    执行输出:
    (1, 2, 30)
    (1, 2, 30, 1, 2, 33, 21, 45, 66)
    

    传两个字典给**kwargs

    def func(**kwargs):
        print(kwargs)
    
    dic1 = {'name':'jack','age':22}
    dic2 = {'name1':'rose','age1':21}
    func(**dic1,**dic2) 
    
    执行输出:
    {'name': 'jack', 'age': 22, 'name1': 'rose', 'age1': 21}
    
    def func(*args,**kwargs):
        print(args)
        print(kwargs)
    
    func(*[1,2,3], *[4,5,6], **{'name':'alex'}, **{'age':18})    #相当于func([1,2,3,4,5,6], {'name':'alex','age':18})
    

    (2)在函数的调用执行时,打散
      *可迭代对象,代表打散(list,tuple,str,dict(键))将元素一一添加到args。
       **字典,代表打散,将所有键值对放到一个kwargs字典里。

    def func(*args,**kwargs):
        print(args,kwargs)
    
    dic1 = {'name':'jack','age':22}
    dic2 = {'name1':'rose','age1':21}
    
    func(*[1,2,3,4],*'asdk',**dic1,**dic2) 
    
    执行输出:(1, 2, 3, 4, 'a', 's', 'd', 'k') {'age1': 21, 'name': 'jack', 'age': 22, 'name1': 'rose'}
    

    形参的顺序:****位置参数 ----> args ----->关键字参数****-------->默认参数**** ------->kwargs*

    *args参数,可以不传,默认为空(),**kwargs 动态传参,他将所有的关键字参数(未定义的)放到一个字典中

    def func(a,b,c,d,*args,e='男',**kwargs):
        print(a,b,c,d,args,e,kwargs)
    
    func(1,2,3,4,5,6,7,v=3,m=7,h=9,e='女')
    
    执行输出:1 2 3 4 (5, 6, 7) 女 {'v': 3, 'h': 9, 'm': 7}
    
    def func(a,b,c,**kwargs):
        print(kwargs)
    func(1,2,r=4,b1=5,c1=6,c=7) 
    
    执行输出:{'r': 4, 'c1': 6, 'b1': 5}
    

    执行没有报错,是因为函数接收参数后,它会从左边到右找,最后找到了c,c=7参数,在a,b,c里面已经定义好了,所以在输出的字典中,并未出现。因为kwargs返回的是未定义的关键字参数。

    如果函数含有多个未知参数,一般使用如下格式:

    def func1(*args,**kwargs):
        pass
    func1() 
    

    二、命名空间和作用域

      当执行函数的时候,他会在内存中开辟一个临时名称空间,存放函数体内的所有变量与值的关系,随着函数的执行完毕,临时空间自动关闭。

    函数里面的变量,在函数外面能直接引用么?****不能

    def func1():
        m = 1
        print(m)
    
    print(m)                          # NameError: name 'm' is not defined
    

    上面为什么会报错呢?现在我们来分析一下python内部的原理是怎么样:

    我们首先回忆一下Python代码运行的时候遇到函数是怎么做的,从Python解释器开始执行之后,就在内存中开辟里一个空间,每当遇到一个变量的时候,就把变量名和值之间对应的关系记录下来,但是当遇到函数定义的时候,解释器只是象征性的将函数名读入内存,表示知道这个函数存在了,至于函数内部的变量和逻辑,解释器根本不关心。等执行到函数调用的时候,Python解释器会再开辟一块内存来储存这个函数里面的内容,这个时候,才关注函数里面有哪些变量,而函数中的变量会储存在新开辟出来的内存中,函数中的变量只能在函数内部使用,并且会随着函数执行完毕,这块内存中的所有内容也会被清空。

    1. 命名空间和作用域

    命名空间:存放”名字与值关系的空间“
    ①全局命名空间:代码在运行时,创建的存储”变量名与值的关系“的内存空间
    ②局部命名空间:在函数调用时临时开辟出来的空间,会随着函数的执行完毕而被清空
    ③内置命名空间:存放了python解释器为我们提供的名字:input,print,str,list,tuple...它们都是我们熟悉 的,拿过来就可以用的方法。

    作用域:就是作用范围
    ①全局作用域:全局命名空间、内置命名空间。在整个文件的任意位置都能被引用、全局有效
    ②局部作用域:局部命名空间,只能在局部范围内生效

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

    取值顺序:
      在局部调用:局部命名空间->全局命名空间->内置命名空间
      在全局调用:全局命名空间->内置命名空间

    综上所述,在找寻变量时,从小范围,一层一层到大范围去找寻。取值顺序:就近原则

    局部变量举例

    name = 'summer'
    def func1():
        name = 'spring'
        print(name)
    func1()
    
    执行输出:spring
    

    取值是从内到外

    name = 'summer'
    def func1():
        print(name)
    func1() 
    
     执行输出:summer
    

    代码从上至下依次执行, 调用函数:函数里面从上至下依次执行。

    print(111)
    def func1():
        print(333)
        func2()
        print(666)
    def func2():
        print(444)
    def func3():
        print(555)
        func2()
    
    func1()
    print(222) 
    
    执行输出:
    111
    333
    444
    666
    222
    
    def f1():
        def f2():
            def f3():
                print("in f3")
            print("in f2")
            f3()
        print("in f1")
        f2()
    f1() 
    
    执行输出:
    in f1
    in f2
    in f3
    

    2. globals和locals方法

    print(globals())         #全局名称空间所有变量,字典
    print(locals())        #局部名称空间所有变量,字典 (当前)
    

    globals()和locals()一般很少用,在函数逻辑比较复杂的情况下,可能会用到。

    li = ['spring', 'summer', 'autumn', 'winter']
    
    def func():
        a = 1
        b = 2
        print('func', globals())
        print('func', locals())
    
        def func1():
            c = 3
            d = 4
            print('func1', globals())
            print('func1', locals())
    
        func1()
    
    func()
    
    输出结果
    func {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x011CC410>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/Users/Administrator/houseinfo/test.py', '__cached__': None, 'li': ['spring', 'summer', 'autumn', 'winter'], 'func': <function func at 0x03542E40>}
    func {'b': 2, 'a': 1}
    func1 {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x011CC410>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/Users/Administrator/houseinfo/test.py', '__cached__': None, 'li': ['spring', 'summer', 'autumn', 'winter'], 'func': <function func at 0x03542E40>}
    func1 {'d': 4, 'c': 3}
    

    (1)global:
    ①在局部命名空间****声明****全局变量

    def func2():
        global name
        name = 'summer'
    
    func2()
    print(name)
    
    执行结果:summer
    

    ②在局部命名空间对全局变量进行****修改****(限于字符串,数字)。

    count = 1
    def func1():
        global count
        count = count + 1
        print(count)
    func1()
    print(count)
    
    执行结果:
    2 
    2
    

    因为全局变量count被函数体的global count 覆盖了

    (2)nonlocal
    ①子函数对父函数的变量进行修改,此变量不能是全局变量

    a = 4
    def func1():
        nonlocal a
        a = 5             #修改全局变量
        #print(name)
    func1()
    print(a) 
    
    执行输出:SyntaxError: no binding for nonlocal 'a' found
    

    ②在局部作用域中,对父级作用域的变量进行引用和修改,并且引用的哪层,从那层及以下此变量全部发生改变。
    例1

    def func1():
        b = 6
        def func2():
            b = 666
            print(b)
        func2()
        print(b)                 #父级不受影响
    func1() 
    
    执行输出:
    666
    6
    

    例2

    def func1():
        b = 6
        def func2():
            nonlocal b          #表示可以影响父级,也就是func1()
            b = 666             #重新赋值
            print(b)
        func2()    
       print(b)                 #这个时候,影响了b的值,输出666
    func1() 
    
    执行输出:
    666
    666
    

    例3

    def aa():                     #不受ccl影响
        b = 42
        def bb():
            b = 10                 #影响子级函数,b都是10
            print(b)
            def cc():
                nonlocal b             #只能影响父级,也就是bb()
                b = b + 20             #b=10+20 也就是30
                print(b)
            cc()
            print(b)
        bb()
        print(b)
    aa()
    
    执行输出:
    10
    30
    30
    42
    

    注意

    a = 5
    def func1():
        a += 1
        print(a)
    func1() 
    

    执行报错。这里函数对全局变量做了改变,是不允许操作的。函数内部可以引用全局变量,不能修改。如果要修改,必须要global一下

    a = 5
    def func1():
        global a
        a += 1
        print(a)
    
    func1()     #输出6
    

    相关文章

      网友评论

        本文标题:Python函数 | 函数详解

        本文链接:https://www.haomeiwen.com/subject/szivuhtx.html