Python: 学习系列之十:函数

系列

函数的特性

  1. 函数也是对象
  2. 一个函数可以作为另一个函数的实参
  3. 一个函数可以作为另一个函数的返回值
  4. 一个函数可以嵌套在另一个函数中
  5. 函数是不支持像java/c#那样重载的
"""
    在python中,一切皆为对象,这里可以看出函数也是对象。

"""
def fun(a):
    print('hello', a)

f =fun
print(f)  # <function fun at 0x000001E5885BC268>

Lambda表达式

"""
    当函数体中只有一行return语句的时,函数的定义可以用一个lambda表达式来代替,其语法格式为:Lambda[形式参数1,形式参数2....]
    lambda 是一个匿名简化版的函数:
        1.没有函数名
        2.没有关键字def,但有lambda
        3.没有小括号
        4.关于形式参数的表达式相当于函数的返回值
"""
print((lambda a, b: a+b)(1, 2))  # 3
result = map(lambda a: a * a, [1, 2, 3, 4])
print(list(result))  # [1, 4, 9, 16]


def do_sth():
    return lambda a, b: a+b


print(do_sth()(1, 2))  # 3

偏函数

"""
   偏函数可以简化函数的调用
   定义函数时,可以给形参设置默认值,从而简化函数的调用,只有与默认值不符的形参才需要传递额外的实参
"""

from functools import partial


def f(a, b=5):
    print('a=', a, 'b=', b)


f_new = partial(f, 2)

f_new()  # a= 2 b= 5
f_new(6)  # a= 2 b= 6
f_new(b=7)  # a= 2 b= 7


def f2(a, b=5, *args, **kwargs):
    print('a=', a, 'b=', b, 'args=', args, 'kwargs=', kwargs)


f2_new = partial(f2, 1, c=6)
f2_new()  # a= 1 b= 5 args= () kwargs= {'c': 6}
f2_new(2, 3)  # a= 1 b= 2 args= (3,) kwargs= {'c': 6}

Python中函数不支持重载

"""
   函数不支持重载

"""
def fun(a):
    print('hello', a)

def fun(a, b):
    print(a, b)


fun('a')  # TypeError: fun() missing 1 required positional argument: 'b'
fun('a', 'b')

闭包

"""
    闭包的构成,必须满足下面三个条件:
        1. 函数(外函数)的内部嵌套定义了另外一个函数(内函数)
        2. 内函数(inner)引用了外函数(outer)中的变量
        3. 函数的返回值是内函数
"""


def outer():
    a = 10
    b = [3]

    def inner():
        # a = 11 # 如果在这里修改变量a, 相当于在内函数中新定义了一个变量a, 把外函数中的变量a给屏幕了
        # a += 1  # 相当于 a = a+1 ,相当于新定义了变量a,但等号后面的a没有定义,所以抛错UnboundLocalError: local variable 'a' referenced before assignment
        # b[0] =5 #这是可以的,因为b是一个可变类型的对象
        print(a)
    return inner


outer()()  # 10


"""
    通常情况下,函数调用结束后,函数内定义的变量将不再可用
"""


def do_sth():
    temp = 8
    print(temp)


do_sth()
# print(temp)  # 报错,NameError: name 'temp' is not defined

"""
    但是对于闭包而言,在外函数调用结束后,外函数中被内函数引用的变量仍然是可用的,因为外函数中被内函数引用的变量会被绑定到内函数的特殊属性__closure__中
"""

ourter_result = outer()


# (<cell at 0x00000287D8DEB888: int object at 0x00007FFAF2F6E470>,),是一个元组
print(ourter_result.__closure__)
# 10, 这里就是在外函数定义的变量,它被内函数引用后就存在这里。
print(ourter_result.__closure__[0].cell_contents)

"""
    通常情况下,不能在内函数中修改外函数中的变量,但如果要修改怎么办呢,使用nonlocal关键字
"""


def out4():
    a = 10

    def inner4():
        nonlocal a  # 这里它不会在内函数中创建一个新的变量,而是直接引用外函数的变量
        a = 11
        print(a)
    return inner4


out4()()  # 11

变量的作用域

"""
    变量的作用域有四种:
    1. 局部作用域(local),比如函数里面定义的变量,函数调用结束后就会被销毁
    2. 嵌套作用域(enclosing), 每次调用嵌套函数中的外函数时,都会创建一个嵌套作用域,当外函数内定义变量时,
       变量的作用域为:从定义变量开始到函数结束。外函数用结束后,其对应的嵌套作用域中的所有变量都会被销毁(闭包除外)。
    3. 全局作用域(global),每次创建一个模块的时,都会创建一个全局作用域,全局作用域为:从定义变量开始到模块结束。
    4. 内置作用域(build-in),每次启用python解释器时都会自动加载内置模块,从而创建一个内置作用域。
       内置模块中的函数(内置函数),可以在程序中直接使用,停止解释后会被销毁。
"""

"""
    当在某个作用域中访问变量时,会按照如上LEGB的顺序依次搜索该作用域及其后面的所有作用域,只要找到了则停止搜索,如果没有找到则抛出NameError。
    因此,如果不同的作用域中定义了同名的变量,根据LEGB的搜索顺序,前面作用域中的变量会屏蔽掉后面的作用域中定义的同名变量。
"""
id ='Global'
def Outside():
    id ='Enclosing'
    def inside():
        id ='Local'
        print(id)
    inside()

Outside()

函数装饰器

"""
    对于某个函数,如果我们不希望在不改变函数代码的情况下,为该函数增加额外的功能,那就可以使用装饰器
    装饰器是一个函数,装饰器接收一个函数作为参数(传入的实参是被装的函数),装饰器的内部嵌套定义另一个函数,
    内函数中会引用装饰器的参数,并且装饰器的返回值是内函数。
    这样,就构成了一个闭包,为了让内函数接收任意类型的参数,将内函数的形参定义为(*args, **kwargs)。
    在函数中,首先完成为被装饰函数添加的功能,然后调用被装饰的函数。
    把装饰器应用到被装饰函数的语法为:在被装饰函数的前面添加"@装饰器的函数名"。
    在被装饰函数add的前面加上@log后,相当于执行了add = log(add), 
    首先,被装饰的函数add会作为实参传递给装饰器log,然后返回装饰器的内函数wrapper,
    最后,将内函数wrapper赋值给名为add(被装饰的函数名)的变量,
    这样,再调用被装饰的函数add时,其实调用的是装饰器的内函数wrapper。
    
"""
def log(func):
    def wrapper(*args, **kwargs):
        print('函数%s被调用了' % func.__name__)
        return func(*args, **kwargs)  # 记住这里传入的参数要带星号
    return wrapper


@log # 在这里加上装器,相当于 add = log(add)
def add(sum1, sum2):
    print(sum1, sum2)
    return sum1+sum2


add(1, 2)  
# 结果如下:
# 函数add被调用了
# 1 2
print(add.__name__) #wrapper 这里实际执行得是wrapper内函数

"""
    如果希望被装饰函数的特殊属性__name__值为其函数名,而不是装饰器的内函数的函数名,
    可以在装饰器的内函数前面添加另外一个装饰器:@functool.wraps(装饰器的参数名)。
    其中functools.wraps指得是标准库模块functools中的函数wraps。

"""

import functools
def log2(func):

    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('函数%s被调用了' % func.__name__)  # 函数add2被调用了
        return func(*args, **kwargs)
    return wrapper


@log2
def add2(sum1, sum2):
    print(sum1, sum2)
    return sum1+sum2


add2(1, 2)
print(add2.__name__)  # add2
"""
    把装饰器应用到被装饰函数时,还可以传递额外的参数。此时,需要编写一个3层嵌套的装饰器。
    对于@log3(2019, 7),相当于执行了 add3 = log2(2019, 7)(add3)。
"""
def log3(month, day):
    def decorator(func):
        print('现在是%i年%i月' % (month, day))

        @functools.wraps(func)
        def wrapper(*args, ** kwargs):
            print('函数%s被调用了' % func.__name__)
            print('现在是%i年%i月' % (month, day))
            return func(*args, **kwargs)
        return wrapper
    return decorator


@log3(2019, 7)
def add3(sum1, sum2):
    print(sum1, sum2)
    return sum1+sum2


add3(1, 2)
# 结果如下:
# 现在是2019年7月
# 函数add3被调用了
# 现在是2019年7月
# 1 2
发布了105 篇原创文章 · 获赞 46 · 访问量 21万+

猜你喜欢

转载自blog.csdn.net/wucong60/article/details/94382956