• Python函数篇


    1.函数名的命名规则

    • 函数名必须以下划线或字母开头,可以包含任意字母、数字或下划线的组合。不能使用任何的标点符号;
    • 函数名是区分大小写的。
    • 函数名不能是保留字。

    2. 形参和实参

    形参:形式参数,不是实际存在,是虚拟变量。在定义函数和函数体的时候使用形参,目的是在函数调用时接收实参(实参个数,类型应与实参一一对应)

    实参:实际参数,调用函数时传给函数的参数,可以是常量,变量,表达式,函数,传给形参   

    区别:形参是虚拟的,不占用内存空间,.形参变量只有在被调用时才分配内存单元,实参是一个变量,占用内存空间,数据传送单向,实参传给形参,不能形参传给实参

    3.参数

    关键字参数:使用参数名提供参数叫做关键字参数。它的主要作用在于可以明确每个参数的作用。关键字参数最厉害的地方在于可以在函数中给参数提供默认值。

    下面例子的必须参数也叫位置参数,因为它们的位置比它们的名字还要重要。

    参数前的星号将所有值放置在同一个元组中。可以说是将这些值收集起来,然后使用。

    两个星号能处理关键字参数的收集操作。

    #必须参数
    def f(name,age):
        print("My name is: %s and my age is: %d"%(name,age))
    f('greg',18)
    
    #关键字参数
    #f(16,'greg')报错
    f(age=16,name='greg')
    
    #默认参数
    def print_info(name, age, sex='male'):
        print('Name:%s' % name)
        print('age:%s' % age)
        print('Sex:%s' % sex)
        return
    print_info('Greg', 18)
    print_info('Wirt', 40, 'female')
    
    #不定长参数
    def add(*args):#加法器
        print(args)
        sum=0
        for i in args:
            sum+=i
        print(sum)
    add(1,2,3,4,5)
    
    #加了星号(*)的变量名会存放所有未命名的变量参数。而加(**)的变量名会存放命名的变量参数
    def p(*args,**kwargs):
        print(args)
        print(kwargs)
        for i in kwargs:
            print('%s:%s' % (i, kwargs[i]))  # 根据参数可以打印任意相关信息了
    p('greg',18,'male',job='IT',hobby="girls")

    参数使用

    def a(*args):
        print(args)
        
    a(*[1, 2, 5])
    
    def b(**kargs):
        print(kargs)
        
    b(**{'name': 'alex'})
    
    
    def c(x, y, d):
        return d(x) + d(y)
    res = c(3, -6, abs)
    print(res)
    
    def foo():
        x=3
        def bar():
            return x
        return bar
    
    def func(name, *args, **kwargs):  # def print_info(name,**kwargs,*args):报错
        print('Name:%s' % name)
        print('args:', args)
        print('kwargs:', kwargs)
        return
    func('greg', 18, hobby='girl', nationality='Chinese', ability='Python')

    四 函数的返回值

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

    注意:

    1. 函数在执行过程中只要遇到return语句,就会停止执行并返回结果,也可以理解为 return 语句代表着函数的结束
    2. 如果未在函数中指定return,那这个函数的返回值为None  
    3. return多个对象,解释器会把这多个对象组装成一个元组作为一个一个整体结果输出。

    五 作用域

    作用域介绍 

    python中的作用域分4种情况:

    • L:local,局部作用域,即函数中定义的变量;
    • E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的;
    • G:globa,全局变量,就是模块级别定义的变量;
    • B:built-in,系统固定模块里面的变量,比如int, bytearray等。 搜索变量的优先级顺序依次是:作用域局部>外层作用域>当前模块中的全局>python内置作用域,也就是LEGB。
    #__author: greg
    #date: 2017/9/6 9:02
    x = int(2.9)  # int built-in
    g_count = 0  # global
    def outer():
        o_count = 1  # enclosing
        def inner():
            i_count = 2  # local
            print(o_count) #1
        # print(i_count) 找不到
        inner()
    outer()
    # print(o_count) #找不到

    当然,local和enclosing是相对的,enclosing变量相对上层来说也是local。

    块级作用域:

    # 块级作用域
    if 1 == 1:
        name = "greg"
    print(name) #greg
    
    for i in range(10):
        age = i
    print(age) #9

    代码执行成功,没有问题;在Java/C#中,执行上面的代码会提示name,age没有定义,而在Python中可以执行成功,这是因为在Python中是没有块级作用域的,代码块里的变量,外部可以调用,所以可运行成功;

    作用域产生 

    >>> x=1
    >>> scope=vars()
    >>> scope['x']
    1
    >>> scope['x']+=1
    >>> x
    2

    在执行x=1后,名称x引用到值1.这就像用字典一样,键引用值,当然,变量和所对应的值用的是这个不可见的字典,实际上这么说已经很接近真实情况了,内建的vars函数可以返回这个字典。这类不可见字典叫做命名空间或者作用域。除了全局作用于外,每个函数调用都会创建一个新的作用域:函数内的变量被称为局部变量。

    全局变量可以使用globals函数获取全局变量值,该函数的近亲是vars,它可以返回全局变量的字典(locals返回局部变量的字典)

    >>> def combine(parameter):
    ...     print(parameter+globals()['parameter'])
    ...
    >>> parameter='berry'
    >>> combine('melon')
    melonberry

    首先看函数的嵌套:

    >>> def foo():
    ...     def bar():
    ...         print("Hello,world")
    ...     bar()
    ...
    >>> foo()
    Hello,world

    嵌套作用域:

    >>> def multiplier(factor):
    ...     def multiplyByFactor(number):
    ...         return number*factor
    ...     return multiplyByFactor
    ...
    >>> double=multiplier(x)
    >>> double(5)
    5
    >>> triple=multiplier(3)
    >>> triple(3)
    9
    >>> multiplier(5)(4)
    20

    类似multiplyByFactor函数存储子封闭作用域的行为叫闭包closure。

    外部作用域的变量一般来说是不能进行重新绑定的。nonlocal关键字被引入。它和global关键字的使用方式类似,可以让用户对外部作用域(但并非全局作用域)的变量进行赋值。

    >>> def foo():x=12
    ...
    >>> x=1
    >>> foo()
    >>> x
    1

    这里的foo函数改变了变量x,但在最后,x并没有改变。因为调用foo,新的命名空间就被创建了,它作用于foo内的代码块。

    在Python中,只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如if、try、for等)是不会引入新的作用域的,如下代码:

    if 2>1:
        x = 1
    print(x)  # 1

    这个是没有问题的,if并没有引入一个新的作用域,x仍处在当前作用域中,后面代码可以使用。

    def test():
        x = 2
    print(x) # NameError: name 'x2' is not defined

    def、class、lambda是可以引入新作用域的。

    >>> x=6
    >>> def f():
    ...     print(x)
    ...     x=5
    ...
    >>> f()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 2, in f
    UnboundLocalError: local variable 'x' referenced before assignment

    >>> def f2():
    ... x=5
    ... print(x)
    ...
    >>> f2()
    5

    错误的原因在于print(x)时,解释器会在局部作用域找,会找到x=5(函数已经加载到内存),但x使用在声明前了,所以报错

     global关键字 

    当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了,当修改的变量是在全局作用域(global作用域)上的,就要使用global先声明一下,

    nonlocal关键字 

    global关键字声明的变量必须在全局作用域上,不能嵌套作用域上,当要修改嵌套作用域(enclosing作用域,外层非全局作用域)中的变量怎么办呢,这时就需要nonlocal关键字了

    代码如下:

    count = 10
    def outer():
        global count
        print(count)    #10
        count = 100
        print(count)    #100
    outer()
    print(count)        #100
    
    #nonlocal
    def outer2():
        count = 10
        def inner():
            nonlocal count
            count = 20
            print(count) #20
        inner()
        print(count)     #20
    outer2()

    总结一下

    (1)变量查找顺序:LEGB,作用域局部>外层作用域>当前模块中的全局>python内置作用域;

    (2)只有模块、类、及函数才能引入新作用域;

    (3)对于一个变量,内部作用域先声明就会覆盖外部变量,不声明直接使用,就会使用外部作用域的变量;

    (4)内部作用域要修改外部作用域变量的值时,全局变量要使用global关键字,嵌套作用域变量要使用nonlocal关键字。nonlocal是python3新增的关键字,有了这个 关键字,就能完美的实现闭包了。 

    作用域链

    name = "greg"
    def f1():
        name = "Eric"
        def f2():
            name = "Snor"
            print(name)
        f2()
    f1() #Snor

    Python中有作用域链,变量会由内到外找,先去自己作用域去找,自己没有再去上级去找,直到找不到报错

    name = "greg"
    # def f1():
    #     print(name)
    # def f2():
    #     name = "eric"
    #     f1()
    # f2()#greg
    
    def f1():
        print(name)
    def f2():
        name = "eric"
        return f1
    ret = f2()
    ret() #greg

    f2()执行结果为函数f1的内存地址,即ret=f1;执行ret()等同于执行f1(),执行f1()时与f2()没有任何关系,name = "greg"与f1()在一个作用域链,函数内部没有变量是会向外找,所以此时变量name值为greg

    六 内置函数

    filter对于序列中的元素进行筛选,最终获取符合条件的序列

    #filter(function,sequence))
    str = ['a', 'b', 'c', 'd']
    def fun1(s):
        if s != 'a':
            return s
    ret = filter(fun1, str)
    print(ret)
    print(list(ret))  # ret是一个迭代器对象
    """
    <filter object at 0x000002C45B43C780>
    ['b', 'c', 'd']
    """
    

    map遍历序列,对序列中每个元素进行操作,最终获取新的序列。map(function, sequence) 

    str = ['a', 'b', 'c', 'd']
    def fun2(s):
        return s + "alvin"
    ret = map(fun2, str)
    print(ret)  # map object的迭代器
    print(list(ret))  # ['aalvin', 'balvin', 'calvin', 'dalvin']

    reduce对于序列内所有元素进行累计操作。reduce(function, sequence, starting_value)

    from functools import reduce
    def add1(x, y):
        return x + y
    print(reduce(add1, range(1, 100)))  ## 4950 (注:1+2+...+99)
    print(reduce(add1, range(1, 100), 20))  ## 4970 (注:1+2+...+99+20)

    对sequence中的item顺序迭代调用function,如果有starting_value,还可以作为初始值调用.

    lambda普通函数与匿名函数的对比

    学习条件运算时,对于简单的 if else 语句,可以使用三元运算来表示,即

    result = 值1 if 条件 else 值2

    如果条件为真:result = 值1
    如果条件为假:result = 值2

    对于简单的函数,也存在一种简便的表示方式,即:lambda表达式

    # 普通函数
    def add(a, b):
        return a + b
    print(add(2, 3)) 
    # 匿名函数
    add = lambda a, b: a + b
    print(add(2, 3))

    面试题1:

    counter = 1
    
    def doLotsOfStuff():
        global counter
        for i in (1, 2, 3):
            counter += 1
            
    doLotsOfStuff()
    print(counter) 

    答案是4 循环3次

    面试题2:

    >>> li = [lambda :x for x in range(10)]
    >>> li
    [<function <listcomp>.<lambda> at 0x000001B5AE9D4378>, <function <listcomp>.<lambda> at 0x000001B5AE9D4400>, <function <listcomp>.<lambda> at 0x000001B5AE9D4488>, <function <listcomp>.<lambda> at 0x000001B5AE9D4510>, <function <listcomp>.<lambda> at 0x000001B5AE9D47B8>, <function <listcomp>.<lambda> at 0x000001B5AE9D4840>, <function <listcomp>.<lambda> at 0x000001B5AE9D48C8>, <function <listcomp>.<lambda> at 0x000001B5AE9D4950>, <function <listcomp>.<lambda> at 0x000001B5AE9D49D8>, <function <listcomp>.<lambda> at 0x000001B5AE9D4A60>]
    >>> print(type(li))
    <class 'list'>
    >>> print(type(li[0]))
    <class 'function'>
    >>> res = li[0]()
    >>> print(res)
    <class 'list'>
    <class 'function'>
    9

    扩展:

    tp=(lambda: x for x in range(10))
    print(type(tp))#<class 'generator'>
    print(next(tp))#<function <genexpr>.<lambda> at 0x0000020BC7973E18>
    print(next(tp)())#1
    print(next(tp)())#2
    print(next(tp)())#3
    print(next(tp)())#4
  • 相关阅读:
    搞定markdown,这一篇就够了
    win32上安装mongodb解决方案
    vue移动端框架到底哪家强
    angular3哪儿去了?angular4与2兼容吗
    angular2入门,就这一篇就够了
    存储过程入门(一)。
    ymPrompt框架。(弹出框)
    c#接口
    c#抽象类和抽象方法
    c#面向对象03
  • 原文地址:https://www.cnblogs.com/ningxin18/p/7954352.html
Copyright © 2020-2023  润新知