zoukankan      html  css  js  c++  java
  • python基础—初识函数(一)

    1.python中函数定义

    函数是逻辑结构化和过程结构化的一种编程方法。

    (1)、python中函数定义方法:

    def test(x):
    
      '''The function definitions'''
    
      x+=1
    
      return x

    说明:

      def :定义函数的关键字

      test :函数名

      ():可定义形参

      '''''':文档描述,非必要

      x+=1: 泛指代码块或程序处理逻辑

      return: 定义返回值

    2.为什么要使用函数

    背景摘要:现在需要写一个监控程序,当CPU,MEMORY,DISK等指标的使用量超过阈值时发送报警邮件

    (1)、传统方式实现:

    while1:
        if cpu>90%:
            #发送邮件提醒
            连接邮箱服务器
            发送邮件
            关闭链接
        
        if memory >90%:
            #发送邮件提醒
            连接邮箱服务器
            发送邮件
            关闭链接
        
        if disk >90%
            #发送邮件提醒
            连接邮箱服务器
            发送邮件
            关闭链接
        

    (2)、使用函数实现:

    def 发送邮件(内容):
      #发送邮件提醒
      连接邮箱服务器
      发送邮件
      关闭链接
    
    while 1:
        if cpu>90%:
            发送邮件('cpu报警'if memory>90%:
            发送邮件('memory报警'if disk>90%:
            发送邮件('disk报警'

    总结使用函数的好处:

      1.代码重用

      2.保持一致性,容易维护

      3.可扩展性

    3.函数参数

    (1)、形参

    形参变量只有在调用时候才分配内存单元,在调用结束时,即刻释放所调用的内存单元。因此,形参只在函数内部有效,函数调用结束返回主调用函数后则不能在使用该形参变量。

    (2)、实参

    实参可以是常量,变量,表达式,函数等,无论实参是何种类型的量,在进行函数调用时,它们必须有确定的值,以便把这些值传给形参。因此应预先用赋值,输入等办法使参数获得确定值。

    def  calc(x,y):   #x,y为形参
        res=x**y
        return res
    
    calc(a,b)   #a,b为实参
    print(calc(a,b))

    (3)、位置参数和关键字

     标准调用:实参和形参一一对应

     关键字调用:位置无需固定

    def test(x,y,z):
        print(x)
        print(y)
        print(z)
    
    test(1,2,3)  #位置标准调用
    test(x=1,y=3,z=2)#关键字调用

     注意:如果混合使用,位置参数一定要在关键字参数的左边

    (4)、默认参数

    def handle(x,type='mysql'):
        print(x,type)
    
    handle('hello')  #type默认值为'mysql'   
    handle('hello',1) #重新赋值,覆盖原本默认参数

    >> hello mysql
       hello 1

     (5)、可变长参数

    ①、*args 

    能够接收列表或元祖

    为了能让一个函数接受任意数量的位置参数。

    def avg(first, *args):
        return (first + sum(rest)) / (1 + len(args))
    
    
    avg(1, 2) # 1.5
    avg(1, 2, 3, 4) # 2.5

    在这个例子中,args是由所有其他位置参数组成的元组。然后我们在代码中把它当成了一个序列来进行后续的计算。

    ②、**kwargs

    为了接受任意数量的关键字参数

    def test(**kwargs)
        print(kwargs)

    test(a=1, b=2) >>{'a': 1, 'b': 2}

    如果希望某个函数能同时接受任意数量的位置参数和关键字参数,可以同时使用*和**。

    def anyargs(*args, **kwargs):
        print(args) # A tuple
        print(kwargs) # A dict

    使用这个函数时,所有位置参数会被放到args元组中,所有关键字参数会被放到字典kwargs中。

    注意:

    一个*参数只能出现在函数定义中最后一个位置参数后面,而 **参数只能出现在最后一个参数。 有一点要注意的是,在*参数后面仍然可以定义其他参数。

    def a(x, *args, y):  #此时y必须是关键字参数
        pass
    
    def b(x, *args, y, **kwargs):   # 此时y必须是关键字参数
        pass

    这种参数就是我们所说的强制关键字参数。

    扩展:只接受关键字参数的函数

    希望函数的某些参数强制使用关键字参数传递,只需将强制关键字参数放到某个*参数或者单个*后面就能达到这种效果。

    def recv(maxsize, *, block):
        'Receives a message'
        pass
    
    recv(1024, True) # TypeError
    recv(1024, block=True) # Ok

    利用这种技术,我们还能在接受任意多个位置参数的函数中指定关键字参数。比如:

    def minimum(*values, clip=None):
        m = min(values)
        if clip is not None:
            m = clip if clip > m else m
        return m
    
    minimum(1, 5, 2, -5, 10) # Returns -5  
    minimum(1, 5, 2, -5, 10, clip=0) # Returns 0

    很多情况下,使用强制关键字参数会比使用位置参数表意更加清晰,程序也更加具有可读性。 例如,考虑下如下一个函数调用:

    msg = recv(1024, False)

    如果调用者对recv函数并不是很熟悉,那他肯定不明白那个False参数到底来干嘛用的。 但是,如果代码变成下面这样子的话就清楚多了:

    msg = recv(1024, block=False)

    另外,使用强制关键字参数也会比使用**kwargs参数更好,因为在使用函数help的时候输出也会更容易理解:

    >>> help(recv)
    Help on function recv in module __main__:
    recv(maxsize, *, block)
        Receives a message

     (6)、局部变量和全局变量

     规则:全局变量名大写,局部变量名小写

     在子程序中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量。

       全局变量作用域是整个程序,局部变量作用域是定义该变量的子程序。
       
     当全局变量与局部变量同名时:
       在定义局部变量的子程序内,局部变量起作用;在其它地方全局变量起作用。
     
      如果函数的内容无global关键字,优先读取局部变量,对全局变量只能读取,无法对全局变量重新赋值,对于可变对象,可以对内部元素进行操作;
      如果函数中有global关键字,变量本质就是全局那个变量,可读取,可赋值。

     如果函数内无global关键字

    #有声明局部变量,函数调用会调用局部变量
    HOBBY = ['上网', '打游戏']
    def test():
        HOBBY = '泡妞'
        print('我的爱好:', HOBBY)
    
    test()   >>我的爱好:泡妞
    #无声明局部变量,函数会调用全局变量
    HOBBY = ['上网', '打游戏']
    def test():
        print('我的爱好:',HOBBY)
    
    test()  >>我的爱好:['上网', '打游戏']

    如果函数中有global关键字

    a = 1
    def test():
        global a
        a += 1    # 改变全局变量a的值
        print('函数中的a:', a) 
        return a
    
    test()
    print(a)

    (7)、返回值

      要想获取函数的执行结果,就可以用return语句把结果返回

      注意:

    1. 函数在执行过程中只要遇到return语句,就会停止执行并返回结果,所以也可以理解为 return 语句代表着函数的结束
    2. 如果未在函数中指定return,那这个函数的返回值为None 

    4、嵌套函数 

    def negan():
        name = 'Negan'
        print(name)
        def alex():
            name = 'Alex'
            print(name)
            def egon():
                name = 'egon'
                print(name)
            print(name)
            egon()
        alex()
        print(name)

    说明:函数只有在被调用的时候才会被执行

    5、递归函数

    在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。

    (1)、使用递归计算n的阶乘

    计算阶乘n! = 1 x 2 x 3 x ... x n,用函数fact(n)表示,可以看出:

    fact(n) = n! = 1 x 2 x 3 x ... x (n-1) x n = (n-1)! x n = fact(n-1) x n

    所以,fact(n)可以表示为n x fact(n-1),只有n=1时需要特殊处理。

    于是,fact(n)用递归的方式写出来就是:

    def fact(n):
        if n==1:
            return 1
        return n * fact(n - 1)

    假如我们要计算5的阶乘,则:

    >>fact(5)
    >>5 * fact(4)
    >>5 * 4 * fact(3)
    >>5 * 4 * 3 * fact(2)
    >>5 * 4 * 3 * 2 * fact(1)
    >>5 * 4 * 3 * 2 * 1

    (2)、递归问路

    import time
    person_list = ['Alex','Bob','Christian']
    def ask_way(person_list):
        print('_'*60)
        if len(person_list) == 0:
            print('没有人知道')
            return '没有人知道'
        person = person_list.pop(0)
        if person == 'Christian':
            return '%s说:我知道,路在脚下' %person
        print('Hi,%s敢问路在何方?' %person)
        print('%s说:我不知道,我可以帮你去问问%s' %(person,person_list))
        time.sleep(1)
        res = ask_way(person_list)
        print('%s问的结果是:%res' %(person, res))
        return res
    
    res = ask_way(person_list)
    print(res)
    ____________________________________________________________
    Hi,Alex敢问路在何方?
    Alex说:我不知道,我可以帮你去问问['Bob', 'Christian']
    ____________________________________________________________
    Hi,Bob敢问路在何方?
    Bob说:我不知道,我可以帮你去问问['Christian']
    ____________________________________________________________
    Bob问的结果是:'Christian说:我知道,路在脚下'es
    Alex问的结果是:'Christian说:我知道,路在脚下'es
    Christian说:我知道,路在脚下

    (3)、递归计算列表中所有元素的和

    item = [1,2,3,4,5,6,7]
    def sum(item)
        head, *tail = item    
        return head + sum(tail) if tail else head

    递归特性:

    a. 必须有一个明确的结束条件

    b. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少

    c. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,

     每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,

     所以,递归调用的次数过多,会导致栈溢出,Python3中默认栈的大小是998)

    6、匿名函数

    Python中可以不用def关键字创建函数,使用lambda即可创建匿名函数,语法格式如下:

    lambda = param1,param2,...,paramN:expression

    匿名函数也是函数,与普通函数一样,参数也是可选的。

    # 普通函数
    def calc(x):
        return x*x
    
    # 改成匿名函数
    lambda x : x*x

    扩展:

    >>> x = 10
    >>> a = lambda y: x + y
    >>> x = 20
    >>> b = lambda y: x + y

    a(10)和b(10)返回的结果是什么?如果你认为结果是20和30,那么你就错了。

    这其中的奥妙在于lambda表达式中的x是一个自由变量, 在运行时绑定值,而不是定义时就绑定,这跟函数的默认值参数定义是不同的。 因此,在调用这个lambda表达式的时候,x的值是执行时的值。例如:

    >>> x = 15
    >>> a(10)
    25
    >>> x = 3
    >>> a(10)
    13

    如果你想让某个匿名函数在定义时就捕获到值,可以将那个参数值定义成默认参数即可,就像下面这样:

    >>> x = 10
    >>> a = lambda y, x=x: x + y
    >>> x = 20
    >>> b = lambda y, x=x: x + y
    >>> a(10)
    20
    >>> b(10)
    30

    在这里列出来的问题是新手很容易犯的错误,有些新手可能会不恰当的使用lambda表达式。 比如,通过在一个循环或列表推导中创建一个lambda表达式列表,并期望函数能在定义时就记住每次的迭代值。例如:

    >>> funcs = [lambda x: x+n for n in range(5)]
    >>> for f in funcs:
    ... print(f(0))
    ...
    4
    4
    4
    4
    4
    >>>

    但是实际效果是运行是n的值为迭代的最后一个值。现在我们用另一种方式修改一下:

    >>> funcs = [lambda x, n=n: x+n for n in range(5)]
    >>> for f in funcs:
    ... print(f(0))
    ...
    0
    1
    2
    3
    4
    >>>

    7、高阶函数

    满足下面两个条件即为高阶函数:

    (1)、函数传入的参数是一个函数名

    (2)、函数的返回值是一个函数

    (1)、把函数当作参数传给另一个函数

    def foo(n):
        print(n)
    def bar(name):
        print('My name is %s'%name)
    
    foo(bar('alex'))   
    
    >>My name is alex
         None

    (2)、返回值中包含函数

    def foo():
        print('from foo')
        return bar
    
    def bar():
        print('from bar')
    
    
    foo()()   
    
    >> from foo
       from bar

    (3)、尾调用

      在函数的最后一步调用另外一个函数(最后一行不一定是函数的最后一步)

      尾调用的关键在于函数的最后一步调用别的函数,根据函数即变量的定义,定义a函数,a函数内调用b函数,b函数内调用函数c,在内存中形成一个调用记录,又称为调用栈,用于保存调用位置以及变量信息,即a->b->c,直到c返回结果给b,c的调用记录才会消失,b返回结果给a,b的调用结果消失,a返回结果,a的调用记录消失,所有的调用记录都是“”先进后出”,形成一个‘’调用栈‘’。

    (4)、map()函数

    map方法接收两个参数,一个是待执行的方法,另一个是可迭代对象。
     
    处理序列中每个元素,得到的结果是一个“列表”,该“列表”元素个数及位置与原来一样。
     
    map()实现原理:
    num = [1,2,3,4]
    
    def add(x):
        return x +1 
    
    def map_test(func, array)
        ret = []
        for i in array:
            ret.append(func(i))
        return ret
    
    print(map_test(add, num))  >> [2,3,4,5]   # 使用普通函数

    print(map_test(lambda x:x+1, num)) >> [2,3,4,5] # 配合匿名函数实现

    使用map函数实现上面的功能

    注意:调用map时候返回的是map类的实例,此时func的方法并没有执行,根据需要返回的类型,在map实例中调用list、tuple、set、dict等方法触发回调函数。

    num = [1,2,3,4]
    
    print(list(map(lambad x: x+1, num)))   

    (5)、filter()函数

    遍历序列中每个元素,判断每个元素得到一个布尔值,如果是True则留下来。

    data = list(range(10))
    
    def func(item):
        if item % 2 == 0:
            return item
    
    def filter_test(func, array):
        ret = [0]
        for i in array:
            if func(i):
                ret.append(func(i))
        return ret

    filter_test(func, data) >> [0,2,3,6,8]

    使用filter()函数实现

    list(filter(lambda x:x%2==0, list(range(10))))

    (6)、reduce()函数

    reduct方法有三个参数:

    reduce(function, sequence, initial=None)

    function:待执行的方法

    sequence:序列

    initial:初始值

    在没有初始值的情况下,首次执行会从序列中取出来两个值,传入function得到一个结果,然后从序列中按照顺序取出下一个值,和该结果一起传入function,直到序列中把所有

    的元素取完。
     
    若设置了初始值,首次执行则会从序列中取出一个元素,并和初始值一起传入function,同样将结果和序列中的下一个值继续传入function,直到序列中的元素被取完。
     
    num = [1,2,3,10]
    
    def multi(x,y):
        return x*y
    
    def reduce_test(func,array,init=None):
        if init is None:
            res = array.pop(0)
        else:
            res = init
        for i in array:
            res = func(res,i)
        return res
    print(reduce_test(multi,num,10))

    使用reduce()实现

    from functools import reduce
    
    num = [1,2,3,10]
    
    reduce(lambda x,y: x*y, num)

    带默认参数

    from functools import reduce
    
    num = [1,2,3,10]
    
    reduce(lambda x,y: x*y, num) >>120
     
  • 相关阅读:
    流程控制之while循环
    流程控制之case语句
    流程控制之if判断
    元字符
    基本数据类型与值操作
    变量
    shell 基础
    node系列---【node连接数据库步骤解析】
    node系列--【 DML语句对数据库的增删改查】
    node系列--【express生成器安装及第三方中间件安装】
  • 原文地址:https://www.cnblogs.com/huiyichanmian/p/8781603.html
Copyright © 2011-2022 走看看