【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)当要构成装饰器的条件时,需要满足:有嵌套、有引用、有返回、有额外功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/788933.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32的Code、RO-data、RW-data、ZI-data和RAM以及flash的关系

对于刚接触stm32编程的同学来说&#xff0c;通常不知道如何选择合适型号的芯片来开发自己的项目&#xff0c;下面就分析一下多大的片内ram以及flash能符合我们项目的需求。 不知道大家有没有注意到&#xff0c;我们的项目每次编译完成后&#xff0c;都会出现这个Program Size&…

【uni-app+Vue3】 API请求封装:让接口调用更便捷

前言&#xff1a;uni-app是一款基于Vue.js框架的跨平台开发工具&#xff0c;可以将代码编译成H5、小程序、App等不同平台的应用。在进行uni-app开发时&#xff0c;网络请求是必不可少的环节。为了方便开发&#xff0c;我们可以封装一些网络请求方法&#xff0c;以便在多个页面中…

数学建模美赛论文文档

目录 1. 摘要&#xff1a;1.1 阅读并理解题目1.2 背景介绍1.3 问题提出 2. 目录&#xff1a;2.1 引言&#xff08;Introduction&#xff09;2.2 假设与合理性说明&#xff08;Assumptions and Justifications&#xff09;2.3 符号说明&#xff08;Notations&#xff09;2.4 模型…

185240-00G 同轴连接器

型号简介 185240-00G是Southwest Microwave的2.92 mm连接器。该连接器采用铍铜合金、工具钢和不锈钢等优质材料&#xff0c;并经过金镀层和钝化处理&#xff0c;确保其稳定可靠&#xff0c;经久耐用。它还兼容欧盟 RoHS 和 WEEE 指令&#xff0c;是一位环保使者&#xff0c;致力…

教师管理小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;教师管理&#xff0c;个人认证管理&#xff0c;课程信息管理&#xff0c;课堂记录管理&#xff0c;课堂统计管理&#xff0c;留言板管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;课程信息&…

Linux 防火墙配置指南:firewalld不同服务管理的应用案例(十个)

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f427;Linux基础知识(初学)&#xff1a;点击&#xff01; &#x1f427;Linux高级管理专栏&#xff1a;点击&#xff01; &#x1f510;Linux中firewalld防火墙&#xff1a;点击&#xff01; ⏰️创作时间&…

高品质标准化诊断肝素诱导性血小板减少症试剂盒—HIT AlertTM Kit

肝素诱导的血小板减少症是一种免疫机制介导的综合征&#xff0c;实验室通常进行免疫学和功能学检测来诊断。免疫学通过检测PF4/肝素抗体来诊断&#xff0c;不考虑其激活血小板能力&#xff0c;比如ELISA&#xff0c;化学发光免疫等检测手段。功能学检测引起肝素依赖性血小板活化…

Scala学习

Scala学习 scala介绍 Scala 是 Scalable Language 的简写&#xff0c;是一门多范式的编程语言 Scala是把函数式编程思想和面向对象编程思想结合的一种编程语言。 大数据计算引擎Spark由Scala编写 scala特点 多范式&#xff1a; 面向对象函数式编程 兼容JAVA: 类库调用互操作 语…

2-1静态库

静态库制作 编写库文件 test.c #include<stdio.h> int main(void){printf("%d\n",add(3,5));return 0; }add.c int add(int a,int b){return ab; }生成.o(目标文件) 用nm查看.o文件 T代表add这个函数的链接性是外部链接&#xff0c;即全局可见&#xff0c;…

从两眼放光到心碎一地《长相思》第二季搞笑爱情转折

这《长相思》第二季的剧情&#xff0c; 简直是心脏按摩器升级版啊&#xff01; 爷爷一开口&#xff0c;要给玱玹安排馨悦当王后 我这小心脏差点就跟着‘嘭’一声 "哎呀&#xff0c;以为要上演宫廷版《速度与激情》 结果小夭女神一出手&#xff0c; 不是醋坛子翻&#…

信创:鲲鹏(arm64)+麒麟(kylin v10)离线部署k8s和kubesphere(含离线部署新方式)

本文将详细介绍&#xff0c;如何基于鲲鹏CPU(arm64)和操作系统 Kylin V10 SP2/SP3&#xff0c;利用 KubeKey 制作 KubeSphere 和 Kubernetes 离线安装包&#xff0c;并实战部署 KubeSphere 3.3.1 和 Kubernetes 1.22.12 集群。 服务器配置 主机名IPCPUOS用途master-1192.168.10…

600Kg大载重起飞重量多旋翼无人机技术详解

600Kg大载重起飞重量的多旋翼无人机是一种高性能的无人驾驶旋翼飞行器&#xff0c;具有出色的载重能力和稳定的飞行特性。该无人机采用先进的飞行控制系统和高效的动力系统&#xff0c;能够满足各种复杂任务的需求&#xff0c;广泛应用于物资运输、应急救援、森林防火等领域。 …

视语坤川大模型智能体平台亮相2024世界人工智能大会

7月4日-7月7日&#xff0c;以“以共商促共享以善治促善智”为主题的2024世界人工智能大会&#xff08;WAIC 2024&#xff09;在上海举办&#xff0c;世界顶级专家学者、知名企业代表、政界人士、高校组织等齐聚上海&#xff0c;共商发展、共话未来。 作为大会的重磅环节——昇…

逻辑运算及其基本概念,定理,算法,规律,卡诺图

逻辑运算及其基本概念&#xff0c;定理&#xff0c;算法&#xff0c;规律&#xff0c;卡诺图 文章目录 逻辑运算及其基本概念&#xff0c;定理&#xff0c;算法&#xff0c;规律&#xff0c;卡诺图开胃小菜运算1、与运算2、或运算3、非运算4、与非&#xff08;都1时为0&#xf…

Science|N型半导体水凝胶(柔性半导体器件/柔性健康监测/导电水凝胶/柔性电子)

2024年5月2日,北京大学雷霆(Ting Lei)课题组在《Science》上发布了一篇题为“N-type semiconducting hydrogel”的论文。论文内容如下: 一、 摘要 水凝胶是一类具有可调机械性能、多样生化功能和良好离子导电性的生物界面材料,但由于缺乏半导体特性,使得水凝胶在电子学中…

数据跨境传输法规日趋完善,企业如何规避合规风险?

随着全球化的发展&#xff0c;跨境数据传输变得日益频繁。在数字化时代&#xff0c;数据安全是企业运营的关键。数据跨境传输由于涉及不同国家和地区&#xff0c;其安全合规性面临着更大的风险和挑战。 2022年&#xff0c;国家网信办发布了《数据出境安全评估办法》&#xff08…

灭世男孩

《血腥复仇与反乌托邦之旅&#xff1a;<灭世男孩>的暴力美学盛宴》 在众多电影的海洋中&#xff0c;有一部独特而引人注目的作品——《灭世男孩》。这部由德国、南非和美国联合制作的电影&#xff0c;以其独特的风格、精彩的动作场面和深刻的主题&#xff0c;为观众带来了…

理解负载组电路-EAK负载电路解释

负载组具有安全、可靠、操作方便、使用寿命长等特点。了解控制、冷却和负载元件电路的布局和功能对于理解负载组的运行、为应用选择负载组和维护负载组非常重要。以下各节将描述这些电路。 EAK负荷组运行概述 负载组接收来自电源的电力&#xff0c;将其转换为热量&#xff0c;…

Hadoop-19 Flume Agent批量采集数据到HDFS集群 监听Hive的日志 操作则把记录写入到HDFS 方便后续分析

章节内容 上一节我们完成了内容&#xff1a; Flume 启动测试Flume Conf编写Flume 测试发送和接收数据 背景介绍 这里是三台公网云服务器&#xff0c;每台 2C4G&#xff0c;搭建一个Hadoop的学习环境&#xff0c;供我学习。 之前已经在 VM 虚拟机上搭建过一次&#xff0c;但…

[AI 大模型] Meta LLaMA-2

文章目录 [AI 大模型] Meta LLaMA-2简介模型架构发展新技术和优势示例 [AI 大模型] Meta LLaMA-2 简介 Meta LLaMA-2 是 Meta 推出的第二代开源大型语言模型&#xff08;LLM&#xff09;&#xff0c;旨在为研究和商业应用提供强大的自然语言处理能力。 LLaMA-2 系列模型包括从…