【Python进阶】拷贝、闭包、装饰器,函数分类

目录

一、对象属性和类属性

1、对象属性

2、类属性

二、类方法和静态方法

1、类方法

2、静态方法

3、扩展综合案例

三、深拷贝和浅拷贝

1、浅拷贝

2、深拷贝

3、浅拷贝和深拷贝的区别

四、函数知识

1、函数的定义与调用

2、函数名记录的是引用

3、函数名当作参数传递

五、闭包

1、闭包的作用

2、使用闭包

3、nonlocal关键字

六、装饰器

1、装饰器入门

1)语法糖

2)传统方式

七、函数分类

1、装饰无参无返回值的函数

2、装饰有参无返回值的函数

3、装饰无参有返回值的函数

4、装饰有参有返回值的函数


一、对象属性和类属性

1、对象属性

对象属性,有时也称为实例属性、普通属性、公有属性,或者直接叫做属性。

在类内部,访问对象属性语法:

self.对象属性名

在类外部,访问对象属性语法:

对象名.对象属性名

例如,一起来完成:

(1)定义一个手机类,属性有品牌、颜色;

(2)分别试着在类内部和类外部访问属性。

# 1.定义手机类
# 2.内部访问
class Phone(object):
    def __init__(self,color,brand):   #  构造器
        # 对象名.属性名
        self.color = color   # 对象属性
        self.brand = brand   # 对象属性
​
    def show(self):
        print(f"访问车的颜色:{self.color}")
​
# 3.外部访问
phone = Phone("黑色","Audi")
print(f"颜色:{phone.color}")
print(f"品牌:{phone.brand}")

总结:

(1)在类外部要访问对象属性,语法:对象名.属性名;

(2)在类内部要访问对象属性,语法:self.属性名。

2、类属性

类属性指的是:类所拥有的属性,在整个类中都可以直接使用。

定义类属性语法:

class 类名(object):

         类属性名 = 值

调用类属性语法:

类名.类属性名

例如,一起来完成:

(1)在Student类中,定义一个名为school_name的类属性;

(2)调用使用类属性,观察效果。

class Student:          # 公共属性就定义成类属性
    school_name = '家里蹲'   #类属性,所有对象共享,不输入任何一个对象,属于整个类,用类名访问,用对象访问可以可以
    # 属性
    def __init__(self, id,name, age,sex):
        self.id = id    # 对象属性,属于某个对象的,使用对象访问
        self.name = name
        self.age = age
        self.sex = sex
​
    # 学习行为
    def study(self):
        print(f'{self.name}学生要好好学习!')
    # 睡觉行为
    def sleep(self):
        print(f'{self.name}学生要好好睡觉!')
​
​
print('-----------------------')
stu = Student(1001,'刘备',18,'M')
​
# 访问类属性
print('-----------访问类属性-----------------')
print(Student.school_name)  # 使用类名来访问类属性
print(stu.school_name)      # 类属性也可以使用对象访问,但是不建议
​
# 访问对象属性
print('-----------访问对象属性-----------------')
print(stu.id)   # 使用对象来访问对象属性
print(stu.name) # 使用对象来访问对象属性
print(stu.age)  # 使用对象来访问对象属性
print(stu.sex)  # 使用对象来访问对象属性# 1.定义类 -类属性  [方法]


class People(object):
    # 类属性
    count = 100
​
    def show(self):
        print("森林防火,人人有责.")
​
# 2.调用使用
# print(People.count)
# 扩展   了解
# p = People()
# print(p.count)
# 思考: 类属性名可以私有化吗?如何访问?

总结:

实际上,可以通过对象名和类名来调用类属性,但优先考虑使用【类名.类属性名】形式。

二、类方法和静态方法

1、类方法

类方法指的是:类所拥有的方法。要形成类方法,需满足:

(1)使用装饰器@classmethod来修饰方法;

(2)把方法的第1个参数设置为cls。

定义类方法,语法:

class 类名(object):
    @classmethod
    def 类方法名(cls):
        ...
        
        
# 类方法中不能出现self

调用类方法,语法:

类名.类方法名()

说明:类方法一般会和类属性配合使用,尤其是私有类属性。

例如,一起来完成:

定义一个小狗类,且小狗很喜欢吃骨头;[类方法]

# 1、定义类方法
class Dog(object):
    total_dogs = 10 # 类属性,类变量
    @classmethod  # 类方法
    def eat(cls):
        print(f'{cls.total_dogs}条狗吃骨头')  # cls就代表类本身 ,使用类方法来访问类属性


# 调用类方法
Dog.eat()  # 直接使用类名来访问类方法

        
# 静态方法中不能出现self

总结:

(1)定义类方法时,需要:先使用@classmethod修饰方法,且第1个参数名为cls;

(2)调用类方法的语法:类名.类方法名()。

2、静态方法

静态方法需要通过装饰器@staticmethod来修饰方法,且静态方法一般不需要定义任何参数。

定义静态方法,语法:

class 类名(object):
    @staticmethod
    def 静态方法名():
        ...

调用静态方法,语法:

类名.静态方法名()

说明:可以使用静态方法显示一些文本信息。

在Python中,静态方法(static method)是类中的一个方法,但它既不属于类本身(如类方法),也不属于类的实例。静态方法不需要特定的类实例来调用,也不需要类本身作为第一个参数(如类方法中的cls)。它们基本上就是定义在类命名空间中的普通函数,只不过这些函数可以通过类名或类的实例名来调用。

例如,一起来完成:

(1)开发一款要显示操作界面的小游戏,分别有开始、暂停、退出等按键;

(2)使用静态方法完成编写。

# 1.定义类
# 2.静态方法
class Game(object):
    @staticmethod
    def show_menu():
        print("==================================")
        print("\t1.开始游戏;")
        print("\t2.暂停游戏;")
        print("\t3.结束游戏.")
        print("==================================")


# 静态方法
Game.show_menu()  # 通过类名来调用静态方法

game = Game() 
game.show_menu() # 通过实例对象调用静态方法

总结:

(1)静态方法要使用@staticmethod修饰,且可以没有参数。

(2)调用静态方法的语法:类名.静态方法名()。

3、扩展综合案例

1. 设计一个 Game 类 (类名)

2. 属性:

                • 定义一个 top_score 类属性 -> 记录游戏的历史最高分

                • 定义一个 player_name 实例属性 -> 记录当前游戏的玩家姓名

3. 方法:

                • 静态方法 show_help() -> 直接打印 这是游戏帮助信息

                • 类方法 show_top_score() -> 显示历史最高分

                • 实例方法 start_game() -> 开始当前玩家的游戏

                -         3.1 输出 玩家 xxx 开始游戏

                -         3.2 使用随机数,生成 10 - 100 之间的随机数字作为本次游戏的得分

                -         3.3 打印 玩家 xxx 本次游戏得分 xxx

                -         3.4 判断本次游戏得分和最高分之间的关系

4. 主程序步骤:

                __main__ 1 查看帮助信息 2 查看历史最高分 3 创建游戏对象,开始游戏

import random

class Game(object):
    top_score = 0  # 类属性:记录游戏的历史最高分

    def __init__(self,name):
        self.name = name  #记录当前游戏的玩家姓名

    #这是游戏帮助信息
    @staticmethod
    def show_help():
        print("==================================")
        print("\t1.开始游戏;")
        print("\t2.暂停游戏;")
        print("\t3.结束游戏.")
        print("==================================")

    #显示历史最高分
    @classmethod
    def show_top_score(cls):
        print(f'当前历史最高分{cls.top_score}')  # 类方法访问类属性

    # 开始当前玩家的游戏
    def start_game(self):
        # 3.1 输出 玩家 xxx 开始游戏
        print(f'玩家{self.name} 开始游戏')
        #3.2 使用随机数,生成 10 - 100 之间的随机数字作为本次游戏的得分
        score = random.randint(10, 100)
        #3.3 打印 玩家 xxx 本次游戏得分 xxx
        print(f'玩家 {self.name} 本次游戏得分 {score}')
        #3.4 判断本次游戏得分和最高分之间的关系
        if score > Game.top_score:
            print(f'恭喜{self.name}打破了最高纪录!')
            Game.top_score = score  # 修改最高分
        else:
            print(f'很遗憾,{self.name}没有打破了最高纪录,继续努力!')


if __name__ == '__main__':
    print('-----------------------------------------')
    #1 查看帮助信息
    Game.show_help()

    #2 查看历史最高分
    Game.show_top_score()

    #3 创建游戏对象,开始游戏
    game = Game('刘备')
    game.start_game()

    print('-----------------------------------------')
    #1 查看帮助信息
    Game.show_help()

    #2 查看历史最高分
    Game.show_top_score()

    #3 创建游戏对象,开始游戏
    game = Game('关羽')
    game.start_game()

三、深拷贝和浅拷贝

1、浅拷贝

浅拷贝需要使用copy模块下的copy()函数。

函数名 含义
copy(t) 使用浅拷贝来拷贝信息。

浅拷贝只对可变类型的第一层对象进行拷贝,对拷贝的对象开辟新的内存空间进行存储,且不会拷贝对象内部的子对象。

例如,一起来完成:

(1)使用浅拷贝来拷贝不可变数据类型:数字19和元组(12, 13, 14, );

(2)使用浅拷贝来拷贝可变数据类型:列表[10, 20, 30]和列表[a, b];

(3)观察拷贝不可变类型、可变类型数据的效果。

总结:

(1)当要了解浅拷贝时,需要使用copy模块的copy()函数;

(2)注意:对于浅拷贝的理解,尽量分为拷贝不可变类型和可变类型的数据来查看。

(3)浅拷贝适用于对象内部主要是基本类型(如int, float, str等)或不需要深拷贝的情况。

2、深拷贝

深拷贝需要使用copy模块下的deepcopy()函数:

函数名 含义
deepcopy(t) 使用深拷贝来拷贝信息。

深拷贝指的是拷贝一个对象时,只要发现对象有可变类型就会对该对象到最后一个可变类型的每一层对象就行拷贝,对每一层拷贝的对象都会开辟新的内存空间进行存储。

通俗地说,深拷贝就是对一个对象中所有层次的拷贝,即既拷贝了引用,也拷贝了内容。

例如,一起来完成:

(1)使用深拷贝来拷贝不可变数据类型:字符串"hello"和元组(100, 200, 300, );

(2)使用深拷贝来拷贝可变数据类型:列表[11, 22, 33]和列表[a, b];

(3)观察拷贝不可变类型、可变类型数据的效果。

#(1)使用浅拷贝来拷贝不可变数据类型:数字19和元组(12, 13, 14, );
import copy

print('-------------不可变类型:数字---------------')
a = 19
b = copy.deepcopy(a)

print(a,b)          # 值相同
print(id(a),id(b))  # 地址相同


print('-------------不可变类型:元组---------------')

tuple1 = (12, 13, 14)
tuple2 = copy.deepcopy(tuple1)

print(tuple1,tuple2)          # 值相同
print(id(tuple1),id(tuple2))  # 地址相同


#(2)使用浅拷贝来拷贝可变数据类型:列表[12, 13, 14]

print('-------------可变类型:列表---------------')
list1 = [12, 13, 14]
list2 = copy.deepcopy(list1)

print(list1,list2)          # 值相同
print(id(list1),id(list2))  # 地址不同

print('-------------可变类型:列表嵌套---------------')
list1 = [12, 13, 14,[77,88,99]]
list2 = copy.deepcopy(list1)

print(list1,list2)          # 值相同
print(id(list1),id(list2))  # 外层地址不同
print(id(list1[3]),id(list2[3]))  # 内层地址竟然相同

list1[3][0] = 777   # list1把内层列表的值修改之后,list2的内层列表也跟着改,这样不合理

print(list1,list2)


print('-------------可变类型:列表嵌套,直接赋值---------------')

list1 = [12, 13, 14,[77,88,99]]
list2 = list1

print(list1,list2)          # 如果直接赋值,值相同
print(id(list1),id(list2))  # 外层地址相同
print(id(list1[3]),id(list2[3]))  # 内层地址也相同

list1[3][0] = 777   # list1把内层列表的值修改之后,list2的内层列表也跟着改,这样不合理

print(list1,list2)

结论:
浅拷贝本质是只拷贝了内存地址值
对不可变类型的数据进行浅拷贝时, 内存地址值相同, 值也相同, 通过指向原有数据内容的内存地址值.
浅拷贝拷贝的是【内存地址值】
浅拷贝了可变数据类型的数据后, 外层内存地址会变化,值也会变化, 内层地址不会变化,值也不会变化

深拷贝既拷贝了内存地址值,又拷贝了内容

对不可变类型的数据进行深拷贝时, 内存地址值相同, 值也相同。

深拷贝拷贝了引用和内容

深拷贝了可变数据类型的数据后, 内存地址值会变化, 值是相同的. [从表面上看: 深浅拷贝效果一致]

3、浅拷贝和深拷贝的区别

对于浅拷贝和深拷贝,区别如下:

函数名 含义
deepcopy(t) 完全拷贝了父对象及其子对象。
copy(t) 拷贝父对象,不会拷贝对象的内部的子对象。

例如,一起来完成:

(1)分别使用浅拷贝和深拷贝来拷贝可变类型:列表[m, n];

(2)观察父对象和子对象引用和数值的变化效果。

#(1)使用浅拷贝来拷贝不可变数据类型:数字19和元组(12, 13, 14, );
import copy

print('-------------不可变类型:数字---------------')
a = 19
b = copy.deepcopy(a)

print(a,b)          # 值相同
print(id(a),id(b))  # 地址相同


print('-------------不可变类型:元组---------------')

tuple1 = (12, 13, 14)
tuple2 = copy.deepcopy(tuple1)

print(tuple1,tuple2)          # 值相同
print(id(tuple1),id(tuple2))  # 地址相同


#(2)使用浅拷贝来拷贝可变数据类型:列表[12, 13, 14]

print('-------------可变类型:列表---------------')
list1 = [12, 13, 14]
list2 = copy.deepcopy(list1)

print(list1,list2)          # 值相同
print(id(list1),id(list2))  # 地址不同

print('-------------可变类型:列表嵌套---------------')
list1 = [12, 13, 14,[77,88,99]]
list2 = copy.deepcopy(list1)

print(list1,list2)          # 值相同
print(id(list1),id(list2))  # 外层地址不同
print(id(list1[3]),id(list2[3]))  # 内层地址竟然相同

list1[3][0] = 777   # list1把内层列表的值修改之后,list2的内层列表也跟着改,这样不合理

print(list1,list2)

结论通俗:

浅拷贝 -> 给文件夹制作了一个快捷方式;

深拷贝 ->复制+粘贴了一份新的, 里面的所有内容都是全新。

总结:

(1)当拷贝多层数据时,才能发现深拷贝、浅拷贝的区别在于是否能完全拷贝子对象;

(2)深拷贝适用于对象内部包含较多非基本类型对象,并且需要确保对象之间完全独立的情况。

深浅拷贝的区别:

浅拷贝只复制了原始对象的引用,而深拷贝则是递归地复制原始对象及嵌套对象,从而得到完全独立的新对象副本

四、函数知识

1、函数的定义与调用

在之前的学习中,已经学习过函数。一起来看看简单和综合函数语法格式。

最简单的语法:

# 定义
def 函数名():
	代码
	...

# 调用
函数名()

综合的语法:

# 定义
def 函数名([参数1, 参数2, 参数3, ...]):
	代码
	...
	[return 值]

# 调用
函数名([值1, 值2, 值3, ...])

接着,再来总结下函数的使用特点

(1)先定义,后调用;

(2)不调用,不执行;

(3)调用一次,执行一次;

(4)当输出有返回值的函数时,输出的是具体的返回值结果;当输出没有返回值的函数时,输出的是None。

例如,一起来完成:

(1)定义并调用一个有返回值的函数func();

(2)定义并调用一个无返回值的函数test()。

# 1.定义有返回值的func()
# def func():
#     print("Hello World..")
#     return 100
#
# # 2.调用
# # func()
# print(func())

# 3.定义无返回值的test()
def test():
    print("人生苦短,我用Python")

# 4.调用
print(test())
# 人生苦短,我用Python
# None

总结:

(1)当调用函数时,要给在调用函数名后添加()括号;

(2)注意:当输出没有返回值的函数调用时,输出的是None;

2、函数名记录的是引用

我们已经知道,当要调用一个函数时,需要:

函数名([值1, 值2, 值3, ...])

说明:要调用函数,记得添加()括号。

那么,如果直接输出函数名,会是什么效果呢?

例如,一起来完成:

(1)定义一个有返回值的函数show();

(2)接着,直接输出函数名,观察输出结果;

(3)结论:函数名记录的是函数引用,即内存地址值。

def show():
    print('我是一个小小的函数')

print(show)  #<function show at 0x000001BF04195168> 函数名就是一个地址引用

# show就是一个地址,指向函数的定义的那片内存空间,把函数的地址赋值为show2,show2也指向了那片内存空间
# show2也称为函数的引用地址
show2 = show  

#使用show2来调用函数
show2()   

总结:

(1)当定义了函数后,就相当于给函数名在内存中开辟了一个新的内存空间;

(2)注意:直接输出函数名时,输出的是函数对应的内存地址值;

3、函数名当作参数传递

我们已经知道,函数名记录的是函数的引用,即内存地址值。

那么,当把函数名直接作为参数进行传递时,从本质上说,传递的是:对应函数的内存地址值。

def 函数名A():
	代码
	...

def function(num):
	代码
	num()  # 调用函数
	...
	
function(函数名A)   # 把函数名当作参数传递

例如,一起来完成:

(1)定义一个无参函数test();

(2)定义有一个参数的函数func();

(3)把无参函数test()的函数名传递给有参函数func(),并观察效果。

# (1)定义一个无参函数test();
# (2)定义有一个参数的函数func();
# (3)把无参函数test()的函数名传递给有参函数func(),并观察效果。

def test():
    print('这个是test函数')

def func(f_name):
    print('func函数开始')
    f_name()
    print('func函数结束')

def add(x,y):
    return  x + y

def func2(f_name):  # f_name = lambda x,y:x+y
    print('func2函数开始')
    a = 10
    b = 20
    print(f'两个数的和是:{f_name(a,b)}')
    print('func2函数结束')

if __name__ == '__main__':
    func(test)

    func2(lambda x,y:x+y)
    func2(add)   # 作用同上

总结:

(1)当把函数名直接传递时,若要查看调用函数的效果,需要在函数内给参数添加()括号进行调用;

(2)把函数名当作参数进行传递,可以应用于闭包和装饰器中。

五、闭包

1、闭包的作用

在之前的学习中,我们会发现:当调用完函数后,函数内定义的变量就销毁了。

如果要保存函数内的变量,就可以使用闭包。

先来看看闭包的作用:闭包可以保存函数内的变量,而不会随着调用完函数而被销毁。

例如,一起来完成:

(1)此处来了解闭包的作用:保存函数内的变量;

(2)定义一个有返回值的函数;

(3)然后,调用函数并使用变量存储返回值结果;

(4)在变量基础上,进行重复累加数值,观察效果。

# 1.定义有返回值的函数
def func():
    a = 10
    print("今天天气真好")
    return a

# 2.使用变量来接收函数调用
# func()
number = func()  # a的值存储到了number  -类似闭包的语法
# 3.观察: 函数虽然调用结束了,但是变量被保存起来  = 闭包
print(number+23)
print(number+44)

2、使用闭包

闭包指的是:在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数。

此时,把这个使用外部函数变量的内部函数,就称为闭包。

定义闭包语法:

# 外部函数
def 外部函数名(外部参数):
    # 内部函数
    def 内部函数名(内部参数):
        ...[使用外部函数的变量]
        代码
    return 内部函数名      # 闭包

调用闭包语法:

变量名 = 外部函数名([外部参数列表])
变量名([内部参数列表])

要构成闭包,需要满足3个条件:

(1)有嵌套:外部函数中嵌套了一个内部函数

(2)有引用:内部函数中使用了外部函数的变量或参数

(3)有返回:外部函数返回内部函数的函数名

说明:

(1)为了更好的理解闭包,建议:外部函数命名为outer、内部函数命名为inner;

(2)当熟练使用闭包后,再任意命名即可。

例如,一起来完成:

(1)定义一个用于求和的闭包;

(2)其中,外部函数有参数x,内部函数有参数y;

(3)然后调用并求解两数之和及输出,观察效果。

# 定义外部函数
def outer(x):
    def inner(y):  # 函数定义嵌套
        result = x + y   # 内部函数使用外部函数变量
        print(f'两个数的和是:{result}')
    return inner   # 外部函数返回内部函数的名字

if __name__ == '__main__':
    inner_func = outer(100)  # 调用外部函数,返回的值就是内部函数的名字,,就是返回一个函数
    inner_func(10)   # 调用内部函数,外部函数的x是100
    inner_func(20)  # 调用内部函数,外部函数的x是100

总结:

(1)构成闭包的3个条件:有嵌套、有引用、有返回;

(2)注意:闭包可以用于处理装饰器的使用。

3、nonlocal关键字

在闭包的使用过程中,当要在内部函数中修改外部函数的变量,需要使用nonlocal提前声明。

nonlocal关键字语法:

nonlocal 变量名

说明:

当变量报错时,记得使用nonlocal声明。

例如,一起来完成:

(1)编写一个闭包,并让内部函数去修改外部函数内的变量a = 100;

(2)记得使用【nonlocal 变量名】提前声明,并观察效果。

# 当你希望在内部函数中修改外边函数的变量,就应该使用nolocal关键字来声明
# 定义外部函数
def outer():
    a = 10    # 外部函数的局部变量
    def inner():  # 函数定义嵌套
        nonlocal a   # 声明这里的a就是外部函数的变量
        a += 1       # 内部函数修改该变量
        print(f'a的值是:{a}')
    return inner   # 外部函数返回内部函数的名字

if __name__ == '__main__':
    inner_func = outer()
    inner_func()

总结:

(1)若要声明能够让内部函数去修改外部函数的变量,则要使用nonlocal关键字;

(2)注意:当要在函数中修改全局变量的值时,要记得使用global给全局变量提前声明。

六、装饰器

1、装饰器入门

1)语法糖

装饰器本质上就是闭包,但装饰器有特殊作用,那就是:在不改变原有函数的基础上,给原有函数增加额外功能。

定义装饰器:

def outer([外面参数列表]):
	def inner([内部参数列表]):
		新增额外功能代码
		....
		...[引用]
	return inner

使用装饰器的标准语法:

# 语法糖
@outer
def 函数名():
	代码
	...

例如,一起来完成:

(1)我们知道,发表评论前是需要先登录的;

(2)接着,先定义有发表评论的功能函数;

(3)使用语法糖方式,在不改变原有函数的基础上,提示用户要先登录~。

# 接下来,要对func函数进行增强,编写闭包
def outer(func):
    def inner():
        print('先要登录!')
        func()
        print('评论成功!')
    return inner  # inner函数本质上就是增强版的func函数

# 定义你要对哪个函数进行增强
@outer
def func():
    print('发表评论!')


if __name__ == '__main__':
    func()

总结:

(1)装饰器本质上就是闭包,作用是在不改变原有函数的基础上,给原有函数增加额外功能;

(2)要构成装饰器,要满足4个条件:有嵌套、有引用、有返回、有额外功能;

2)传统方式

定义装饰器:

def outer([外面参数列表]):
	def inner([内部参数列表]):
		新增额外功能代码
		....
		...[引用]
	return inner

使用装饰器的传统方式语法:

# 传统方式
变量名 = outer([外面参数列表])
变量名([内部参数列表])

例如,一起来完成:

(1)我们知道,发表评论前是需要先登录的;

(2)接着,先定义有发表评论的功能函数;

(3)使用传统方式,在不改变原有函数的基础上,提示用户要先登录~;

(4)了解装饰器的执行流程。

# 接下来,要对func函数进行增强,编写闭包
def outer(func):
    def inner():
        print('先要登录!')
        func()
        print('评论成功!')
    return inner  # inner函数本质上就是增强版的func函数

# 定义你要对哪个函数进行增强
def func():
    print('发表评论!')


if __name__ == '__main__':
    inner_func = outer(func)
    inner_func()

总结:

(1)使用装饰器时,应该优先考虑使用:语法糖;

(2)为了更好的了解装饰器的执行流程,可以通过装饰器的传统方式来了解。

七、函数分类

对于函数的使用,可以根据有无参数、有无返回值来进行分类。分为:

(1)无参无返回值的函数

(2)有参无返回值的函数

(3)无参有返回值的函数

(4)有参有返回值的函数

无参无返回值的函数的语法:

# 定义
def 函数名():
	代码
	...
	
# 调用
函数名()

有参无返回值的函数的语法:

# 定义
def 函数名(参数1, 参数2, ...):
	代码
	...
	
# 调用
函数名(值1, 值2, ...)

无参有返回值的函数的语法:

# 定义
def 函数名():
	代码
	...
	return 值
	
# 调用
变量名 = 函数名()

有参有返回值的函数的语法:

# 定义
def 函数名(参数1, 参数2, ...):
	代码
	...
	return 值
	
# 调用
变量名 = 函数名(值1, 值2, ...)

总结:

(1)我们会发现,函数的分类有4种,那么对应于装饰器也有4种;

(2)注意:对函数来分类,主要是根据有无参数和返回值来划分的。

1、装饰无参无返回值的函数

当使用装饰器装饰无参无返回值的函数时,语法:

def outer(func):
	def inner():
		新增额外功能代码
		....
		...[引用]
	return inner

例如,一起来完成:

(1)在给无参无返回值的原有函数求和计算结果之前;

(2)添加一个友好提示(注意:不能改变源码):正在努力计算中...。

# 1.定义装饰器
def outer(func):
    def inner():   # 有嵌套
        print("=======正在努力计算中...========")   # 有额外功能
        func()  # 有引用
    return inner  # 有返回

# 2.使用装饰器
# 3.定义函数
@outer
def get_sum():
    a = 10
    b = 23
    sum = a + b
    print(f"两数之和为:{sum}")

# 4.调用函数
get_sum()

总结:

(1)当被装饰的函数没有参数时,对应定义的装饰器的内部函数也没有参数;

(2)注意:当被装饰的函数没有返回值时,对应定义的装饰器的内部函数也没有返回值.。

2、装饰有参无返回值的函数

当使用装饰器装饰有参无返回值的函数时,语法:

def outer(func):
	def inner(参数1, 参数2, ...):
		新增额外功能代码
		....
		...[引用]
	return inner

例如,一起来完成:

(1)在给有参无返回值的原有函数求和计算结果之前;

(2)添加一个友好提示(注意:不能改变源码):正在努力计算中...。

def outer(func):
    def inner(x,y):   # 有嵌套

总结:

当被装饰的原有函数有参数时,装饰器的内部函数也有对应个数的参数

3、装饰无参有返回值的函数

当使用装饰器装饰无参有返回值的函数时,语法:

def outer(func):
	def inner():
		新增额外功能代码
		....
		...[引用]
		return 值  # 要返回值
	return inner

例如,一起来完成:

(1)在给无参有返回值的原有函数求和计算结果之前;

(2)添加一个友好提示(注意:不能改变源码):正在努力计算中...。
 

# 1.定义装饰器
def outer(func):
    def inner():
        print("=====正在努力计算中...")
        result = func()
        return result
    return inner

# 2.使用装饰器
# 3.定义函数
@outer
def get_sum():
    a = 19
    b = 23
    sum = a + b
    return sum

# 4.调用函数
print(get_sum())

总结:

当原有函数有返回值时,记住:装饰器的内部函数也需要返回结果,否则没有输出效果。

4、装饰有参有返回值的函数

当使用装饰器装饰有参有返回值的函数时,语法:

def outer(func):
	def inner(参数1, 参数2, ...):
		新增额外功能代码
		....
		...[引用]
		return 值  # 要返回值
	return inner

例如,一起来完成:

(1)在给有参有返回值的原有函数求和计算结果之前;

(2)添加一个友好提示(注意:不能改变源码):正在努力计算中...。

# 接下来,要对func函数进行增强,编写闭包
def outer(func):
    def inner(a,b):  #被增强的函数func,有两个参数,则这里的inner也必须有两个参数,因为inner是增强版的func
        print('求和前!')
        result = func(a,b)
        return result     # 被增强函数有返回值,这里也必须有返回值 这里的return,一般返回被增强函数的返回值
    return inner  # inner函数本质上就是增强版的func函数

# 定义你要对哪个函数进行增强
@outer
def func(a,b):   # 有参无返回值
    return a+b


if __name__ == '__main__':
    result = func(10,20)
    print(result)

总结:

(1)当被装饰的原有函数有参有返回值时,定义的装饰器类型应该在内部函数中要有参数,也要有返回值;

(2)当要构成装饰器的条件时,需要满足:有嵌套、有引用、有返回、有额外功能。

相关推荐

  1. Python基础学习(8)函数-/装饰

    2024-07-10 10:36:03       50 阅读
  2. python -装饰

    2024-07-10 10:36:03       33 阅读
  3. Python高级函数技术:装饰与回调

    2024-07-10 10:36:03       27 阅读
  4. Python笔记11-装饰和设计模式

    2024-07-10 10:36:03       56 阅读
  5. Python--装饰高级应用

    2024-07-10 10:36:03       25 阅读
  6. 深入理解Python中的装饰

    2024-07-10 10:36:03       24 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-10 10:36:03       99 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-10 10:36:03       107 阅读
  3. 在Django里面运行非项目文件

    2024-07-10 10:36:03       90 阅读
  4. Python语言-面向对象

    2024-07-10 10:36:03       98 阅读

热门阅读

  1. 10.pwn ROP(栈溢出攻击的核心)

    2024-07-10 10:36:03       33 阅读
  2. sklearn基础教程

    2024-07-10 10:36:03       29 阅读
  3. 跨境支付新篇章:引领电商潮流

    2024-07-10 10:36:03       33 阅读
  4. CSS学习

    2024-07-10 10:36:03       26 阅读
  5. Unity 常用取整方法

    2024-07-10 10:36:03       29 阅读
  6. 华为机考真题 -- 攀登者1

    2024-07-10 10:36:03       28 阅读
  7. Linux内核 -- 内存管理之scatterlist结构使用

    2024-07-10 10:36:03       51 阅读