Python函数的基本使用

一、函数的定义

函数的基本使用:
    什么是函数:
        在程序中,具备某一功能的‘工具’指的就是函数,‘事先准备工具’的过程即函数的定义,‘拿来就用’即函数的调用。

    为什么要用函数:
        1、组织结构不清晰,可读性差
        2、代码冗余
        3、可维护性、扩展性差

    函数的定义:
        函数的使用必须遵循’先定义,后调用’的原则。
        函数的定义就相当于事先将函数体代码保存起来,然后将内存地址赋值给函数名,函数名就是对这段代码的引用,这和变量的定义是相似的。没有事先定义函数而直接调用,就相当于在引用一个不存在的’变量名’。
            def 函数名(参数1,参数2,...):
                """文档描述"""
                函数体
                return1、def: 定义函数的关键字;
            2、函数名:函数名指向函数内存地址,是对函数体代码的引用。函数的命名应该反映出函数的功能;
            3、括号:括号内定义参数,参数是可有可无的,且无需指定参数的类型;
            4、冒号:括号后要加冒号,然后在下一行开始缩进编写函数体的代码;
            5、"""文档描述""":描述函数功能,参数介绍等信息的文档,非必要,但是建议加上,增强函数的可读性;
            6、函数体:由语句和表达式组成;
            7、return 值:定义函数的返回值,return是可有可无的。

        定义函数的三种方式:
            无参函数:
                在函数体内没有需要传入的参数
                    def func():
                        pass
            有参函数:
                在函数体内有需要传入的参数
                    def func(x,y):
                        print(x,y)
            空函数:
                函数体代码为pass
                    def func(x, y):
                        pass

        定义函数的过程:
            1、申请内存空间保存函数体代码
            2、将上述内存地址绑定函数名
            3、定义函数不会执行函数体代码,但是会检测函数体语法

        调用函数的过程:
            1、通过函数名找到函数的内存地址
            2、然后加口号就是在触发函数体代码的执行

        调用函数的三种方式:
            1、语句的形式:
                直接加括号调用即可;  foo()
            2、表达式的形式:
                m=func(1,2)            #将调用函数的返回值赋值给x
                n=10*func(1,2)        #将调用函数的返回值乘以10的结果赋值给

            3、函数调用可以当做参数
                res=add(add(1,2),10)   #将函数作为参数传入
                print(res)

        函数的返回值:
            return是函数结束的标志,即函数体代码一旦运行到return会立刻终止函数的运行,并且会将return后的值当做本次运行的结果返回,一般情况下返回值有三种情况:
                1、返回None
                    表示函数体内没有return或者定义return为空
                2、返回一个值
                    return3、返回多个值:
                    用逗号分隔开多个值,会被return返回成元组

 

二、函数的参数

函数的参数:
    函数的参数分为形式参数和实际参数,简称形参和实参
        形参即在定义函数时,括号内声明的参数。形参本质就是一个变量名,用来接收外部传来的值;
        实参即在调用函数时,括号内传入的值,实参值可以是常量、变量、表达式或三者的组合;

    形参与实参的关系:
        1、在调用阶段,实参(变量值)会绑定给形参(变量名
        2、这种绑定关系只能在函数体内使用
        3、实参与形参的绑定关系在函数调用时生效,函数调用结束后解除绑定关系

    位置参数:
        按照从左到右的顺序依次定义的参数称之为位置参数
            def register(name,age,sex):                       #定义位置形参:name,age,sex,三者都必须被传值
                print('Name:%s Age:%s Sex:%s' %(name,age,sex))
            register()                          #若不传值,则会抛出异常TypeError:缺少3个位置参数 
        在调用函数时,按照从左到右的顺序依次定义实参,称为位置实参,凡是按照这种形式定义的实参会按照从左到右的顺序与形参一一对应

    关键字参数:
        在函数调用阶段,按照key=value的形式传入的值,凡是按照这种形式定义的实参,可以完全不按照从左到右的顺序定义,但仍能为指定的形参赋值
            register(sex='male',name='lili',age=18)
                # Name:lili  Age:18  Sex:male
        需要注意在调用函数时,实参也可以是按位置或按关键字的混合使用,但必须保证关键字参数在位置参数后面,且不可以对一个形参重复赋值

    默认参数:
        在定义函数阶段,就已经被赋值的形参,称之为默认参数,默认参数可以在调用阶段不为其赋值
            def register(name,age,sex='male'):     #默认sex的值为male
                print('Name:%s Age:%s Sex:%s' %(name,age,sex))
        虽然默认参数在定义阶段就已经为形参赋值,但是若在调用阶段想修改的情况下,只需重新赋值即可
        虽然默认值可以被指定为任意数据类型,但是不推荐使用可变类型,通常应设为不可变类型
        默认参数必须在位置参数之后
        默认参数的值仅在函数定义阶段被赋值一次
            x = 1
            def foo(args=x):
                print(args)
            x = 2         #定义阶段args已被赋值为1,此处的修改与默认参数args无任何关系
            foo()
        函数最理想的状态:函数的调用只跟函数本身有关系,不被外界代码的影响

    可变长度的参数(*与**的用法)
        可变长度指的是在调用函数时,传入的值(实参)的个数不固定
        而实参是用来为形参赋值的,所以对应着,针对溢出的实参必须有对应的形参来接收
        可变长度的位置参数:
            *形参名:用来接收溢出的位置实参,溢出的位置实参会被*保存成元组的格式然后赋值紧跟其后的形参名
            *后跟的可以是任意名字,但是约定俗成应该是args
            *可以用在实参中,实参中带*,先把*后的值打散成位置实参
            如果在传入多个值的时候没有加*,那就只是一个普通的位置参数
            如果形参为常规的参数(位置或默认),实参仍可以是*的形式

        可变长度的关键字参数
            **形参名:用来接收溢出的关键字实参,**会将溢出的关键字实参保存成字典格式,然后赋值给紧跟其后的形参名
            **后跟的可以是任意名字,但是约定俗成应该是kwargs
            **可以用在实参中(**后跟的只能是字典),实参中带**,先把**后的值打散成关键字实参
            如果在传入多个值的时候没有加**,那就只是一个普通的位置参数
            如果形参为常规参数(位置或默认),实参仍可以是**的形式

    命名关键字参数:
        在定义函数时,*后定义的参数,称之为命名关键字参数
        命名关键字实参必须按照key=value的形式为其传值
            def func(x,y,*,a,b):        # 其中,a和b称之为命名关键字参数
                print(x,y)
                print(a,b)
        此外,如果形参中已经有一个args了,命名关键字参数就不再需要一个单独的*作为分隔符号了

    组合使用:
        综上所述所有参数可任意组合使用,但定义顺序必须是:位置参数、默认参数、args、命名关键字参数、*kwargs
        可变参数*args与关键字参数kwargs通常是组合在一起使用的,如果一个函数的形参为*args与kwargs,那么代表该函数可以接收任何形式、任意长度的参数
            >>> def wrapper(*args,**kwargs):
            ...     pass
        注意:*args、**kwargs中的args和kwargs被替换成其他名字并无语法错误,但使用args、kwargs是约定俗成的。

 

三、名称空间与作用域

名称空间与作用域:
    什么是名称空间:
        名称空间即存放名字与对象映射/绑定关系的地方,是对栈区的划分
        有了名称空间之后,就可以在栈区中存放相同的名字,详细的,名称空间

    名称空间的分类:
        内建名称空间:
            伴随python解释器的启动/关闭而产生/回收,因而是第一个被加载的名称空间,用来存放一些内置的名字,比如内建函数名
            存活周期:python解释器启动则产生,python解释器关闭则销毁

        全局名称空间:
            伴随python文件的开始执行/执行完毕而产生/回收,是第二个被加载的名称空间,文件执行过程中产生的名字都会存放于该名称空间中
            存活周期:python文件执行则产生,python文件运行完毕后销毁

        局部名称空间:
            伴随函数的调用/结束而临时产生/回收,函数的形参、函数内定义的名字都会被存放于该名称空间中
            存活周期:在调用函数时存活,函数调用完毕后则销毁

        名称空间的加载顺序:
            内置名称空间->全局名称空间->局部名称空间
        名称空间的查找顺序:
            局部名称空间->全局名称空间->内置名称空间
        名称空间的"嵌套"关系是以函数定义阶段为准,与调用位置无关

    什么是作用域:
        作用域指的是生效的作用范围
    作用域的分类:
        全局作用域:内置名称空间、全局名称空间
            位于全局名称空间、内建名称空间中的名字属于全局范围,该范围内的名字全局存活(除非被删除,否则在整个文件执行过程中存活)、全局有效(在任意位置都可以使用)
        局部作用域:局部名称空间的名字
            位于局部名称空间中的名字属于局部范围。该范围内的名字临时存活(即在函数调用时临时生成,函数调用结束后就释放)、局部有效(只能在函数内使用)

    作用域与名字查找的优先级:
        局部作用域:
            先查找局部作用域,没有再查找全局作用域
                x=100 #全局作用域的名字x
                def foo():
                    x=300 #局部作用域的名字x
                    print(x) #在局部找x
                foo()#结果为300

        全局作用域:
            先查找全局名称空间,没有找到,再查找内置名称空间,再没有找到,则抛出异常
                x=100
                def foo():
                    x=300 #在函数调用时产生局部作用域的名字x
                foo()
                print(x) #在全局找x,结果为100
        注:
            可以调用内建函数locals()和globals()来分别查看局部作用域和全局作用域的名字,查看的结果都是字典格式。在全局作用域查看到的locals()的结果等于globals()

        Python支持函数的嵌套定义,在内嵌的函数内查找名字时,会优先查找自己局部作用域的名字,然后由内而外一层层查找外部嵌套函数定义的作用域,没有找到,则查找全局作用域

        在函数内,无论嵌套多少层,都可以查看到全局作用域的名字,若要在函数内修改全局名称空间中名字的值,当值为不可变类型时,则需要用到global关键字;若实参的值为可变类型时,函数体内对该值的修改将直接反应到原值,

            x=1
            def foo():
                global x #声明x为全局名称空间的名字
                x=2
            foo()
            print(x) #结果为2

        对于嵌套多层的函数,使用nonlocal关键字可以将名字声明为来自外部嵌套函数定义的作用域(非全局)
            def  f1():
                x=2
                def f2():
                    nonlocal x
                    x=3
                f2() #调用f2(),修改f1作用域中名字x的值
                print(x) #在f1作用域查看x

            f1()    #结果为3
        nonlocal x会从当前函数的外层函数开始一层层去查找名字x,若是一直到最外层函数都找不到,则会抛出异常。

 

四、全局变量和局部变量

#局部变量:函数内部的变量是局部变量,作用域仅在函数内部可见(局部命名空间)
#全局变量:函数外部的变量是全局变量,作用域横跨整个文件(全局命名空间)
#内置函数:内建命名空间

-- globals() :返回字典,存放着全局作用域所有内容
-- locals()  :返回字典,当前作用域所有内容(locals调用之前的变量)
-- global    :关键字:声明全局变量获修改全局变量
-- nonlocal  :关键字:修改局部变量(当前函数上一层的局部变量)

 

五、函数对象和闭包

函数对象和闭包:
    函数对象:
        函数对象指的是函数可以被当做’数据’来处理
        即函数可以被引用,可以作为容器类型的元素,可以作为容器类型的元素,可以作为参数传入另一个函数,也可以作为另一个函数的返回值
    函数嵌套:
        在一个函数内定义其它的函数
    闭包函数:
        闭包函数=名称空间与作用域+函数嵌套+函数对象
        核心点:名字的查找关系是以函数定义阶段为准
        什么是闭包函数:
            ""函数指的该函数是内嵌函数
            ""函数指的该函数包含对外层函数作用域名字的引用(不是对全局作用域)
            “闭”代表函数是内部的,“包”代表函数外’包裹’着对外层作用域的引用。因而无论在何处调用闭包函数,使用的仍然是包裹在其外层的变量。
                x=1
                def outer():
                    x=2
                    def inner():
                        print(x)
                    return inner

                func=outer()
                func() # 结果为2

        可以通过函数的closure属性,查看到闭包函数所包裹的外部变量
            >>> func.__closure__
            (<cell at 0x10212af78: int object at 0x10028cca0>,)
            >>> func.__closure__[0].cell_contents
            2
        闭包函数的用途:
            两种为函数体传值的方法:
                一、直接将函数值以参数的形式传入
                    def get(url):
                        return requests.get(url).text

                二、利用闭包函数,将值包给函数
                    def page(url):
                        def get():
                            return requests.get(url).text
                        return get
            闭包函数的这种特性有时又称为惰性计算。使用将值包给函数的方式,在接下来的装饰器中也将大有用处

 

posted @ 2020-07-18 21:31  adcwb  阅读(162)  评论(0编辑  收藏  举报