Python:函数、闭包、装饰器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cp5E1CFH-1596363312765)(F:\JianShu_material\Python\图片\函数\函数.png)]

1. 函数的定义与使用

def greet_user():
    """显示简单的问候语""" 
    print("Hello!") 
    
greet_user() #Hello!

这个示例演示了最简单的函数结构。使用关键字def来告诉Python你要定义一个函数。

定义函数时,要向Python指出了函数名,还可能在括号内指出函数需要的参数。

在这里,函数名为greet_user() ,它不需要任何参数,因此括号是空的(即便如此,括号也必不可少)。

最后,定义以冒号结尾。

函数在进行使用时必须要带上“()”,否则无法使用。

2. 函数的参数

2.1 位置参数

你调用函数时,Python必须将函数调用中的每个实参都关联到函数定义中的一个形参。

为此,最简单的关联方式是基于实参的顺序。这种关联方式被称为位置实参。

def describe_pet(animal_type, pet_name): 
    """显示宠物的信息""" 
    print("\nI have a " + animal_type + ".") 
    print("My " + animal_type + "'s name is " + pet_name.title() + ".") 
    
#相同的位置相对应,因此位置实参的实参位置很重要。
describe_pet('hamster', 'harry')
"""I have a hamster.
My hamster's name is Harry."""

2.2 默认参数

编写函数时,可给每个形参指定默认值 。在调用函数中给形参提供了实参时,Python将使用指定的实参值;

否则,将使用形参的默认值。因此,给形参指定默认值后,可在函数调用中省略相应的实参。

def describe_pet(pet_name, animal_type='dog'):
    """显示宠物的信息""" 
    print("\nI have a " + animal_type + ".") 
    print("My " + animal_type + "'s name is " + pet_name.title() + ".") 

# 调用这个函数时,如果没有给animal_type 指定值,Python将把这个形参设置为'dog'。
describe_pet(pet_name='willie')
"""I have a dog. 
My dog's name is Willie."""

由于给animal_type 指定了默认值,无需通过实参来指定动物类型,因此在函数调用中只包含一个实参——宠物的名字。

然而,Python依然将这个实参视为位置实参,因此如果函数调用中只包含宠物的名字,这个实参将关联到函数定义中的第一个形参。

这就是需要将pet_name 放在形参列表开头的原因所在。

因此上述结果也可用另种方式表示:

describe_pet('willie')

如果要描述的动物不是小狗,可使用类似于下面的函数调用:

describe_pet(pet_name='harry', animal_type='hamster')

由于显式地给animal_type 提供了实参,因此Python将忽略这个形参的默认值。

2.3 关键字参数

关键字实参是传递给函数的名称对。你直接在实参中将名称和值关联起来了,因此向函数传递实参时不会混淆。

def describe_pet(animal_type, pet_name): 
    """显示宠物的信息""" 
    print("\nI have a " + animal_type + ".") 
    print("My " + animal_type + "'s name is " + pet_name.title() + ".") 
    
describe_pet(animal_type='hamster', pet_name='harry')
"""
I have a harry. 
My harry's name is Hamster."""

关键字实参的实参顺序不重要。

2.4 可变参数

简单可变参数:*args

有时候,你预先不知道函数需要接受多少个实参,Python允许函数从调用语句中收集任意数量的实参。

def make_pizza(*toppings): 
    """打印顾客点的所有配料""" 
    print(toppings) 

make_pizza('pepperoni') #('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese') #('mushrooms', 'green peppers', 'extra cheese')

形参名toppings 中的星号让Python创建一个名为toppings 的空元组,并将收到的所有值都封装到这个元组中。

可以将位置参数、关键字参数和可变参数相结合。

此时必须在函数定义中将接纳任意数量实参的形参放在最后。

Python先匹配位置实参和关键字实参,再将余下的实参都收集到最后一个形参中。

def make_pizza(size, *toppings):
    """概述要制作的比萨""" 
    print("\nMaking a " + str(size) + "-inch pizza with the following toppings:") 
    for topping in toppings:
        print("- " + topping) 
        
make_pizza(16, 'pepperoni') 
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')

"""Making a 16-inch pizza with the following toppings: 
- pepperoni 
Making a 12-inch pizza with the following toppings:
- mushrooms 
- green peppers 
- extra cheese"""

键值对可变参数:**kwargs

有时候,需要接受任意数量的实参,但不知道这些参数是什么类型的数据。

在这种情况下,可将函数参数编写成能够接受任意数量的键值对。

def build_profile(first, last, **user_info):
    """创建一个字典,其中包含我们知道的有关用户的一切"""
    profile = {}
    profile['001'] = first
    profile['002'] = last
    for key, value in user_info.items():
        profile[key] = value
        return profile

user_info = {'003':'王五'}
user_profile = build_profile('张三', '李四', **user_info)
print(user_profile) #{'001': '张三', '002': '李四', '003': '王五'}

形参user_info 中的两个星号让Python创建一个名为user_info 的空字典,并将收到的所有名称—值对都封装到这个字典中。

在这个函数中,可以像访问其他字典那样访问user_info 中的键值对。

编写函数时,你可以以各种方式混合使用位置实参、关键字实参和任意数量的实参。

3. 函数返回值

3.1 简单返回值

def get_formatted_name(first_name, last_name): 
    """返回整洁的姓名"""  
    full_name = first_name + ' ' + last_name 
    return full_name.title()  
musician = get_formatted_name('jimi', 'hendrix') 
print(musician) #Jimi Hendrix  

3.2 返回字典

函数可返回任何类型的值,包括列表和字典等较复杂的数据结构。

def build_person(first_name, last_name):
    """返回一个字典,其中包含有关一个人的信息""" 
    person = {'first': first_name, 'last': last_name} 
    return person 
musician = build_person('jimi', 'hendrix') 
print(musician) #{'first': 'jimi', 'last': 'hendrix'}

3.3 返回值特点

  1. 若返回了x个结果,且有x个接收值,则返回值与接收值之间一一对应
  2. 若返回了x个结果,但只有1个接收值,则返回值形成元组赋值给唯一的接收值

4. 变量与内部函数

4.1 局部变量与全局变量

局部变量

声明在函数内部,仅限函数内部使用

全局变量

声明在函数外部,所有变量可以访问

局部变量可以在函数内部修改,当全局变量不可变时,不可随意在函数内部修改,要使用global

例如list全局变量是可变的,字符串全局变量是不可变的

4.2 内部函数

内部函数是指定义在函数内部的函数

内部函数的特点:

  1. 内部函数可以访问外部函数的变量
  2. 内部函数可以修改外部函数的可变变量
  3. 内部函数修改全局变量的不可变变量时,需要在内部函数声明:global 变量名
  4. 内部函数修改外部函数的不可变的变量时,需要在内部函数中声明:nonlocal 变量名
  5. locals()查看本地变量有哪些,以字典的形式输出
  6. globals()查看全局变量有哪些,以字典形式输出,存在系统键值对
def func1():
    # 声明变量
    n = 100 # 局部变量
    list = [1,3,6,0]

    def func2():
        # 使用nonlocal使内部函数可以使用局部不可变变量
        nonlocal n
        y = 0
        for i in list:
            list[y] += n
            y += 1

        n += 1

    func2()
    print(n)
    print(list)

func1()
#101
#[101, 103, 106, 100]
# 全局变量
a = 100
print(globals())

def func():
    #局部变量
    b = 99

    def func1():
        # 对于全局变量a的声明必须在内部函数中
        global a
        nonlocal b
        c = 98

        c += 1
        b += 1
        a += 1

        print(c)
        print(b)
        print(a)

    func1()
    print(locals())#{'func1': <function func.<locals>.func1 at 0x000001B927A290D8>, 'b': 100}

func()
"""99
100
101"""

5. 函数的闭包

5.1 闭包的概念

外部函数通过return将内部函数返回的形式叫做闭包

形成条件:

  1. 外部函数中定义了内部函数
  2. 外部函数是有返回值
  3. 返回的值是:内部函数名
  4. 内部函数引用了外部函数的变量
def func1():
    a = 100
    def func2():
        b = 99
        print(a,b)

    return func2

x = func1()
print(x) #<function func1.<locals>.func2 at 0x00000273B56290D8>
x() #100 99

5.2 闭包保存参数状态

def func1(a, b):
    c = 1
    def func2():
        s = a + b + c
        print(s)

    return func2

x = func1(10,4)
y = func1(3, 7)

y() #11
x() #15

6. 装饰器

6.1 装饰器的概念

装饰器的特点:

  1. 函数作为参数传递给另一函数
  2. 要有闭包的特点
  3. 原函数func1带参数,则wrapper函数必须带参数,并传给内部的func1
def decorate(func):
    a = 100
    print('进行操作1')

    def wrapper(): #若参数为(*args,**kwargs)则为万能装饰器
        func()
        print("进行操作2")

    print('进行操作3')

    return wrapper

# 使用装饰器
@decorate
def func1():
    print('进行操作4')

"""
进行操作1
进行操作3
"""
func1()
"""
进行操作1
进行操作3
进行操作4
进行操作2
"""
"""
1.func1被装饰函数
2.将被装饰函数作为参数传给装饰器decorate
3.执行decorate函数
4.将decorate函数返回值又赋值给house
"""

6.2 装饰器的参数问题

原函数带参数

原函数的参数问题分为以下几种情况:

  1. 原函数带一个参数: wrapper函数必须带参数
  2. 原函数带未知个参数: wrapper函数带参数*args
  3. 原函数带默认参数: wrapper函数带参数**kwargs
  4. 无论原函数带什么参数: 都可使用(*args,**kwargs)

装饰器带参数

带参数的装饰器是三层的,最外层的函数负责接收装饰器参数,里面的内容还是原装饰器的内容

# 装饰器带参数
def outer(a): # 第一层
    def decorate(func): # 第二层
        def wrapper(*args,**kwargs): # 第三层
            func(*args)
            print('---->铺地砖{}快'.format(a))

        return wrapper # 返回出来的是:第三层
    return decorate # 返回出来的是:第二层

@outer(10)
def house(time):
    print('我{}日期拿到房子'.format(time))

@outer(100)
def stree():
    print('修建街道')

house('2020-07-18')
stree()
"""我2020-07-18日期拿到房子
---->铺地砖10快
修建街道
---->铺地砖100快"""

6.3 多层装饰器

一个原函数可使用多个装饰器修饰,先执行最近的装饰器,将其内部函数作为另一个装饰器的参数进行执行下一个装饰器

7. 匿名函数

7.1 匿名函数的格式

lambda 参数1,参数2 : 运算

# 匿名函数
s = lambda a,b : a + b

print(s)
result = s(1,2) #<function <lambda> at 0x00000176A4AFFAF8>
print(result) #3

def func(a,b, func1):
    result = func1(a,b)
    print(result)
   
func(2,5,lambda x,y : x*y) #10
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值