Python全栈开发之函数

发布时间:2019-08-05 14:59:52编辑:auto阅读(1393)

    No.1 函数介绍

    所谓函数,就是把具有独立功能的代码块组织为一个小模块,在需要的时候调用

    函数的使用有两个步骤:
    1、定义函数
    2、调用函数

    函数的作用,代码重用,提高开发效率

    No.2 定义和调用

    定义函数的格式如下:

    def 函数名():
    
        函数封装的代码
    

    def是英文define的缩写

    函数名称应该能够表达函数封装代码的功能,方便后续的调用

    函数名称的命名应该符合标识符的命名规则

    函数调用:

    通过函数名()即可完成函数的调用

    No.3 普通参数

    def func(name): # name是形式参数
        print(name) # 函数体
    
    func('kernel') # 执行函数,'kernel'是实参

    No.4 默认参数

    定义了默认参数后,在函数调用时不再需要传入,默认参数放在最后面

    def info(name,age,country='China') # name,age是普通参数,country是默认参数
        print('name:',name)
        print('age:',age)
        print('国家:',country)
    
    info('kernel',21) # 调用时,没有传入country,就使用默认参数

    No.5 关键参数

    正常情况下,给函数传递参数需要按照定义的顺序,不想按顺序就要使用关键参数,但是关键参数必须放在普通参数之后

    def info(name,age,country='China') # name,age是普通参数,country是默认参数
        print('name:',name)
        print('age:',age)
        print('国家:',country)
    info(age=21,name='kernel') # 使用关键参数,可以不按照顺序

    No.6 元素和字典拆包

    *args

    def demo(*args):
        print(args) # ('kernel', 21, '山东')
    demo("kernel",21,"山东")

    **kargs

    def demo(**kwargs):
        print(kwargs) # {'name': 'kernel', 'age': 21, 'address': '山东'}
    demo(name="kernel",age=21,address="山东")

    No.7 递归函数

    特点:

    函数内部调用自己

    函数内部的代码是相同的,只是参数不同,处理结果不同

    当参数满足一个条件时,函数不再执行

    栗子
    data = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35]
    def binary_search(dataset,find_num):
        if len(dataset) >1:
            mid = int(len(dataset)/2)
            if dataset[mid] == find_num:  #find it
                print("找到数字",dataset[mid])
            elif dataset[mid] > find_num :# 找的数在mid左面
                print("找的数在mid[%s]左面" % dataset[mid])
                return binary_search(dataset[0:mid], find_num)
            else:# 找的数在mid右面
                print("找的数在mid[%s]右面" % dataset[mid])
                return binary_search(dataset[mid+1:],find_num)
        else:
            if dataset[0] == find_num:  #find it
                print("找到数字啦",dataset[0])
            else:
                print("没的分了,要找的数字[%s]不在列表里" % find_num)
    binary_search(data,66)

    No.8 函数的可变参数和不可变参数

    在函数中,针对参数使用赋值语句,会不会影响函数调用时的实参呢?

    不会,只要针对参数使用赋值语句,无论是可变类型还是不可变类型,都会在函数修改参数的引用,不会影响到外部变量的引用

    def demo(num, num_list):
        print("start")
        # 赋值语句
        num = 200
        num_list = [4,5,6]
        print("id=%d,num=%d"%(id(num),num))
        print("id=",id(num_list),"num_list=",num_list)
        print("end")
    
    gl_num = 100 # id=1875081376,gl_num=100
    gl_list = [1,2,3] # id= 2164478175240 gl_list [1, 2, 3]
    print("id=%d,gl_num=%d"%(id(gl_num),gl_num)) # id=1875084576,num=200
    print("id=",id(gl_list),"gl_list",gl_list) # id= 2164477982152 num_list= [4, 5, 6]
    demo(gl_num, gl_list)
    print("id=%d,gl_num=%d"%(id(gl_num),gl_num)) # id=1875081376,gl_num=100
    print("id=",id(gl_list),"gl_list",gl_list) # id= 2164478175240 gl_list [1, 2, 3]

    如果传递的参数是可变类型,在函数内部,使用方法修改了数据的内容,同样会影响到外部的数据

    def demo(name_list):
        name_list.append('end')
        print(name_list)
    name_list = ['kernel']
    print("id=",id(name_list),"name_list",name_list) # id= 1980496082376 name_list ['kernel']
    demo(name_list)
    print("id=",id(name_list),"name_list",name_list) # id= 1980496082376 name_list ['kernel', 'end']
    demo(name_list)
    print("id=",id(name_list),"name_list",name_list) # id= 1980496082376 name_list ['kernel', 'end','end']
    我们发现,外部变量的数据已经被改变了,但是它的引用一直没有改变
    再看一个栗子
    def demo(name_list=[]):
        name_list.append('end')
        print(name_list)
    demo() # ['end']
    demo() # ['end', 'end'] 
    demo() # ['end', 'end', 'end']
    demo() # ['end', 'end', 'end', 'end']
    这不是我们想要的结果,那怎么办呢?
    我们只需要将形参改成一个不可变类型就可以了
    def demo(name_list=None):
        name_list = []
        name_list.append('end')
        print(name_list)
    demo() # ['end']
    demo() # ['end']
    demo() # ['end']
    demo() # ['end']

    No.9 高阶函数

    满足下列条件之一就可以称为高阶函数

    • 存在一个参数为函数
    • 函数的返回值中存在函数

    map函数

    map函数是Python内置的高阶函数,它接收一个函数和一个可迭代对象,并将函数作用在可迭代对象的每个元素上,返回一个map对象

    def func(x):
        return x * x
    
    r = map(func,range(10))
    print(list(r)) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

    reduce函数

    reduce函数也是Python内置的高阶函数,同样它接收一个函数和一个可迭代对象,但是函数必须要接收两个参数,reduce对可迭代对象的每个对象调用函数,并返回最终结果

    栗子1
    from functools import reduce
    def func(x,y):
        return x * y
    
    r = reduce(func,range(1,10))
    print(r)
    调用过程func(func(func(func(func(func(func(func(1,2),3),4),5),6),7),8),9)
    栗子2
    from functools import reduce
    def func(x,y):
        return x * y
    
    r = reduce(func,range(1,10),10)
    print(r)
    调用过程func(func(func(func(func(func(func(func(func(1,10),2),3),4),5),6),7),8),9)

    filter函数

    filter函数也是Python内置的高阶函数,同样它接收一个函数和一个可迭代对象,函数的作用是对每个元素进行判断,返回True和False,filter根据判断结果自动过滤掉不符合条件的元素

    def func(x):
        if x % 2 == 0:
            return x
    
    r = filter(func,range(10))
    print(list(r))

    lambda函数

    没有函数名

    单条语句

    语句的执行结果就是返回值

    name_list = ['kernel','alex','qiyue','hobby','eric','aomikee']
    r = sorted(name_list)
    print(r)
    r = sorted(name_list,key=lambda x:len(x))
    print(r)

    No.10 函数闭包

    内部函数

    def outer():
        print('outer is running。。。')
        def inner():
            print('inner is running。。。')
        inner()
    outer()
    outer is running。。。
    inner is running。。。
    内部函数inner存在于outer函数作用域内部,所以如果在outer外部区域调用inner函数就会报错

    关于闭包

    def outer():
        x = 10
        y = 10
        def inner():
            print(x+y)
        return inner
    
    fun = outer()
    fun() # 20
    如果试图在一个内部函数里对外部函数的变量进行引用,这就是Python的闭包,由于闭包基于内部函数,所以闭包同样不能在外部进行调用

    修改外部函数的变量

    def outer():
        x = 10
        y = 10
        def inner():
            x = x + 1
            print(x+y)
        return inner
    
    fun = outer()
    fun() # UnboundLocalError: local variable 'x' referenced before assignment

    因为闭包是基于内部函数的,所以说将会启动Python解释器的屏蔽机制,这时候Python解释器就认为x是内部函数的局部变量,我们此举正是试图对不存在的变量进行修改操作,所以报在定义之前引用错误,那么,怎么解决这个问题呢

    Python2

    如果外部函数作用域的变量是可变类型的,那么它就不会被Python解释器屏蔽掉
    def outer():
        x = [10]
        y = 10
        def inner():
            x[0] = x[0] + 1
            print(x[0]+y)
        return inner
    
    fun = outer()
    fun() # 21

    Python3

    使用nonlocal关键字
    def outer():
        x = [10]
        y = 10
        def inner():
            nonlocal x
            x = x + 1
            print(x+y)
        return inner

    No.11 作用域

    块级作用域

    想想下面的程序会运行成功吗?为什么?

    for i in range(10):
        pass
    
    print(i) # 9
    代码执行成功,在Java/C#等强类型语言中,执行上面的语言会提示i没有定义,而在Python中确是可以执行的,那是因为Python中没有块级作用域,代码内的变量,外部可以调用

    局部作用域

    def say_hello():
        name = 'kernel'
        print('hello ',name)
    
    print(name) # NameError: name 'name' is not defined
    运行报错,name变量只在函数中起作用,所以全局是无法调用的

    作用域链

    Python存在作用域链,首先从自己的作用域找,如果没有就去一级一级的往上找,如果没有找到就报错

    name = "Alex"
    def f1():
        name = "Eric"
        def f2():
            name = "Kernel"
            print(name)
        f2()
    
    f1() # Kernel

    栗子

    name = "kernel"
    def f1():
        print(name)
    
    def f2():
        name = "eric"
        f1()
    
    f2() # Kernel
    为什么会输入Kernel而不是eric呢?
    那是因为函数在未执行时,作用域链就已经形成了,所以f1会去全局变量中找name这个变量而不是f2
    r = ([lambda :x for x in range(10)])
    
    print(type(r))
    for i in r:
        print(type(i))
        print(i())
    返回的结果是一个列表类型,列表的每个元素是一个函数,所有的函数运行出来都是9,为什么呢?
    那是因为函数在没有执行的时候,内部代码是不会执行的

关键字