Python面向对象进阶

思维导图

Python面向对象进阶的思维导图

私有权限

私有属性

为了更好的限制属性的访问和包含隐私,可以给属性设置私有权限

当把属性设置为私有属性后,则该属性只能被本类直接访问。

定义私有属性语法:

设置和获取私有属性值语法:

代码案例

例如,一起来完成:

(1)Master把技术传承给徒弟时,不想把自己的私房钱(¥6000000)继承给徒弟;

(2)这时,就要为私房钱这个属性设置私有权限;

(3)思考:若想要来访问私有属性,该怎么做呢?

# 例如,一起来完成:
# (1)Master把技术传承给徒弟时,不想把自己的私房钱(¥6000000)继承给徒弟;
# (2)这时,就要为私房钱这个属性设置私有权限;
# (3)思考:若想要来访问私有属性,该怎么做呢?#1.
class Master(object):def __init__(self):# self.money = 6000000 # 这么写,会被继承,不是私有的self.__money = 6000000 # 这么写,不会被继承,是私有的# 如下的代码就看需求了,对外提供一个公共的方法来访问私有属性moneydef get_attribute(self):return self.__moneydef set_attribute(self,money):# 从外部传入money(金额),覆盖之前原有money(金额)if money <=0:print("盗亦有道...")else:self.__money += money
# 非私有的会被继承给徒弟,私有的就不会被继承给徒弟。
class Tudi(Master):pass#2.#3.
if __name__ == '__main__':t1 = Tudi()# print(t1.money) # 6000000# print(t1.money) # 'Tudi' object has no attribute 'money'# 访问父类公共的方法,以此间接访问父类私有属性print(t1.get_attribute())# 演示设置父类私有属性t1.set_attribute(-1000) # 设置值print(t1.get_attribute()) # 获取设置后的新的结果

总结

当要定义私有属性时,仅需要在属性名前添加双下划线; 

注意:如果要从外部访问私有属性值,需要在类中定义set/get方法。 

私有方法

当把方法设置为私有方法后,则该方法只能被本类直接访问。

定义私有方法语法:

代码案例

例如,一起来完成:

(1)Master把技术传承给徒弟时,不想把自己独创调料配方的制作过程继承给徒弟;

(2)这时,就要为制作独创配方这个方法设置私有权限;

(3)思考:该怎么访问私有方法呢? 

# 例如,一起来完成:
# (1)Master把技术传承给徒弟时,不想把自己独创调料配方的制作过程继承给徒弟;
# (2)这时,就要为制作独创配方这个方法设置私有权限;
# (3)思考:该怎么访问私有方法呢?#1.
class Master(object):def __init__(self):self.name = "master"#这是一个对门配方的方法,不希望继承给徒弟,怎么办呢?# 只需要在前面加2个下划线即可,表示私有方法,只能在类内部访问,外部无法访问。def __special_process(self):print("放点辣条")print("放点酱油...")print("放点其他配方...")class Tudi(Master):passif __name__ == '__main__':t1 = Tudi()# t1.special_process() #t1.special_process() # 'Tudi' object has no attribute 'special_process'

总结

当把方法设定为私有权限后,则该方法不会被继承给子类;

注意:当子类继承了父类后,子类拥有父类的非私有属性和非私有方法。 

对象属性和类属性

对象属性

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

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

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

代码案例

例如,一起来完成:

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

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

# 例如,一起来完成:
# (1)定义一个手机类,属性有品牌、颜色;
# (2)分别试着在类内部和类外部访问属性。#1.
class Phone(object):def __init__(self,brand,color):self.brand = brandself.color = colordef show(self):# 在类的内部访问属性print(self.brand)print(self.color)
#2.测试代码
if __name__ == '__main__':# 创建对象p1 = Phone('Apple',"black")p1.show()# 在类外部访问属性print(p1.brand)print(p1.color)# 结论:在类内部访问属性,self.属性名# 在类外部访问属性,对象名.属性名,不能使用类名.属性名来访问。# 思考:能不能通过类来访问呢?# 类名.属性名print(Phone.brand) # type object 'Phone' has no attribute 'brand'

总结

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

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

类属性

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

定义类属性语法:

调用类属性语法:

代码案例

例如,一起来完成:

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

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

(3)使用对象stu1调用类属性,观察效果。

(4)使用对象stu2调用类属性,观察效果。

(5)思考:stu1修改了类属性,stu2也会跟着修改吗? 

# 例如,一起来完成:
# (1)在Student类中,定义一个名为school_name的类属性;
# (2)使用类名调用类属性,观察效果。
# (3)使用对象stu1调用类属性,观察效果。
# (4)使用对象stu2调用类属性,观察效果。
# (5)思考:stu1修改了类属性,stu2也会跟着修改吗?#1.
class Student(object):school_name = "MIT" # 类属性,伴随着类而存在的,和对象没关系。
#2.#3.
if __name__ == '__main__':# print(Student.school_name)# 使用对象访问stu1 = Student()# 对象访问类属性。stu1.school_name = 'UC' # 这是在创建一个变量,和类属性的变量名同名而已。print(stu1.school_name) # UC# 使用stu2对象调用stu2 = Student()print(stu2.school_name) # MIT# 结论:类属性是独一份的,不会更改。# 应用:积累一些全局唯一的属性信息。

 总结

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

类方法和静态方法

类方法

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

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

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

定义类方法,语法:

调用类方法,语法:

说明:

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

代码案例

例如,一起来完成:

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

(2)仔细观察,小狗有属于自己独有的犬牙个数tooth=42;[私有类属性]

(3)思考:若要从外部访问小狗的犬牙,该怎么做呢?

# 例如,一起来完成:
# (1)定义一个小狗类,且小狗很喜欢吃骨头;[类方法]
# (2)仔细观察,小狗有属于自己独有的犬牙个数tooth=42;[私有类属性]
# (3)思考:若要从外部访问小狗的犬牙,该怎么做呢?#1.
class Dog(object):__teeth = 42# 对象方法# def eat(self):#     print("dog eating bones...")# 对象方法# @classmethod是一个装饰器。目前只需要这么写就可以。装饰器下午再讲。@classmethoddef eat(cls):print(cls.__teeth)print("dog eating bones...")# cls的含义:当前类# self的含义:当前对象# super的含义:父类print(cls) # <class '__main__.Dog'># 扩展写法,不建议拿对象方法来修改,而是直接写@classmethoddef show(cls):print("建议的写法,直接写就可以")#2.#3.
if __name__ == '__main__':# Dog.eat() # eat() missing 1 required positional argument: 'cls'Dog.eat() # dog eating bones...# 结论:类方法是所有对象都有的公共方法。他一般用来提供一些固定的功能。不需要对象参与。# 其次,也可以结合私有属性来用。# cls的含义:当前类# self的含义:当前对象# super的含义:父类

 总结

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

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

静态方法

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

定义静态方法,语法:

调用静态方法,语法:

说明:

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

代码案例

例如,一起来完成:

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

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

# 例如,一起来完成:
# (1)开发一款要显示操作界面的小游戏,分别有开始、暂停、退出等按键;
# (2)使用静态方法完成编写。#1.
class Game(object):def show1(self):"""这是一个对象方法:return: 无"""print("-------1.开始-------")print("-------2.暂停-------")print("-------3.退出-------")@classmethoddef show2(cls):"""这是一个类方法:return: 无"""print("-------1.开始-------")print("-------2.暂停-------")print("-------3.退出-------")@staticmethoddef show3():"""这是一个静态方法:return: 无"""print("-------1.开始-------")print("-------2.暂停-------")print("-------3.退出-------")# 扩展写法@staticmethoddef show4():print("直接写即可")
#2.
if __name__ == '__main__':# 使用类来调用# 调用对象方法、类方法、静态方法# Game.show1()Game.show2()print("abc" * 30)Game.show3()# 使用对象来调用print("abc" * 30)g1 = Game()g1.show1()g1.show2()g1.show3()# 结论:静态方法是一种特殊的类方法。它不需要类中其他的(属性、方法)来支撑。

总结

定义静态方法时,需要:先使用@staticmethod修饰方法,且可以没有参数

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

面向对象综合案例

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 创建游戏对象,开始游戏

# 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 randomclass Game(object):#1.定义属性# 类属性top_score = 0# 对象属性(实例属性、属性)def __init__(self,player_name):self.player_name = player_name#2.定义方法# 静态方法@staticmethoddef show_help():print("这是游戏的帮助信息")# 类方法@classmethoddef show_top_score(cls):print(f"历史最高分为:{cls.top_score}")# 对象方法def start_game(self):# 3.1 输出 玩家 xxx 开始游戏print(f"玩家【{self.player_name}】开始游戏")# 3.2 使用随机数,生成 10 - 100 之间的随机数字作为本次游戏的得分score = random.randint(10,100)# 3.3 打印 玩家 xxx 本次游戏得分 xxxprint(f"玩家【{self.player_name}】本次游戏得分:{score}")# 3.4 判断本次游戏得分和最高分之间的关系if score > Game.top_score:Game.top_score = scoreprint(f"恭喜【{self.player_name}】破纪录...")else:print("继续努力,下次再见...")if __name__ == '__main__':# 1.查看帮助信息Game.show_help()# 2.查看历史最高分Game.show_top_score()# 3.创建游戏对象,开始游戏player1 = Game("张三")player1.start_game()player2 = Game("张三丰")player2.start_game()player3 = Game("张无忌")player3.start_game()player4 = Game("张翠山")player4.start_game()Game.show_top_score()

深拷贝和浅拷贝

浅拷贝

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

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

代码案例

例如,一起来完成:

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

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

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

# 例如,一起来完成:
# (1)使用浅拷贝来拷贝不可变数据类型:数字19和元组(12, 13, 14);
# (2)使用浅拷贝来拷贝可变数据类型:列表[10, 20, 30] ;
# (3)观察拷贝不可变类型、可变类型数据的效果。import copy# 1.
num = 19
t1 = (12, 13, 14)
num2 = copy.copy(num)
print(num2) # 19
t2 = copy.copy(t1)
print(t2) # (12, 13, 14)
# 2.
list1 = [10, 20, 30]
list2 = copy.copy(list1)
print(list2) # [10, 20, 30]
# 3.
# 浅拷贝也可以拷贝数据。

总结

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

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

深拷贝

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

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

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

代码案例

例如,一起来完成:

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

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

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

# 例如,一起来完成:
# (1)使用深拷贝来拷贝不可变数据类型:字符串"hello"和元组(100, 200, 300);
# (2)使用深拷贝来拷贝可变数据类型:列表[11, 22, 33] ;
# (3)观察拷贝不可变类型、可变类型数据的效果。import copy# 1.
str1 = "hello"
t1 = (100, 200, 300)
str2 = copy.deepcopy(str1)
print(str2) # hello
t2 = copy.deepcopy(t1)
print(t2)
# 2.
list1 = [11, 22, 33]
list2 = copy.deepcopy(list1)
print(list2) # [11, 22, 33]
# 3.

总结

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

注意:对深拷贝、浅拷贝的理解,可以通过深浅了解拷贝得多一些。 

浅拷贝和深拷贝的区别

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

代码案例

例如,一起来完成:

(1)分别使用浅拷贝和深拷贝来拷贝可变类型:列表[11,22,33, [44,55]];

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

# 例如,一起来完成:
# (1)分别使用浅拷贝和深拷贝来拷贝可变类型:列表[11,22,33, [44,55]];
# (2)观察父对象和子对象引用和数值的变化效果。import copy# 1.
list1 = [11, 22, 33, [44, 55]]
# 浅拷贝
# list2 = copy.copy(list1)
# print(list2) # [11, 22, 33, [44, 55]]
# # print(list2[3][0]) # 44
# list2[3][0] = 440
# print(list2) # [11, 22, 33, [440, 55]]
# print(list1) #[11, 22, 33, [440, 55]]
# print(id(list1[3]))
# print(id(list2[3]))
# 结论:浅拷贝,只拷贝对象的第一层。里(深)层的对象无法拷贝。
# 深拷贝
print("-" * 30)
list3 = copy.deepcopy(list1)
print(list3) # [11, 22, 33, [44, 55]]
list3[3][0] = 444
print(list3) # [11, 22, 33, [444, 55]]
print(list1) # [11, 22, 33, [44, 55]]
print(id(list1[3]))
print(id(list3[3]))
# 结论:深拷贝,无论对象是多少层,都可以拷贝,也就是说,深层的对象也可以拷贝。
# 2.

总结

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

请问:深拷贝是完全拷贝,拷贝了父对象,拷贝了子对象 

函数知识

函数的定义与调用

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

最简单的语法:

综合的语法:

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

 代码案例

例如,一起来完成:

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

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

# 例如,一起来完成:
# (1)定义并调用一个有返回值的函数func();
# (2)定义并调用一个无返回值的函数test()。#1.
def func():return 10#2.
def test():print("没有返回值的test函数")# print(func()) # 10
# test() # 没有返回值的test函数
# print(test()) # 没有返回值的test函数 \n None#思考:如果调用函数时,没有扩号,会发送什么呢?
# func
print(func) # 0x000001D7FEBED310

总结

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

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

函数名记录的是引用

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

说明:

        要调用函数,记得添加()括号

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

代码案例

例如,一起来完成:

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

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

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

# 例如,一起来完成:
# (1)定义一个有返回值的函数show();
# (2)接着,直接输出函数名,观察输出结果;
# (3)结论:函数名记录的是函数引用,即内存地址值。#1.
def show():return 10
#2.
print(show) # 0x0000020DED07D310
#3.

总结

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

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

函数名当作参数传递

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

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

 代码案例

# 例如,一起来完成:
# (1)定义一个无参函数test();
# (2)定义有一个参数的函数func();
# (3)把无参函数test()的函数名传递给有参函数func(),并观察效果。#1.
def test():print('无参的test函数....')#2.
# def func(xxxx):
#     print(xxxx) # 0x00000235281FD310
#     # print(id(xxxx))#3.
# 调用func函数,使用数字传递(数字也是有内存地址值,本质上传递的也是内存地址值)
# num = 10
# func(num)
# print(id(num))
# print(id(10))# 调用func函数,使用函数名传递,函数名纪录的也是内存地址值(引用),本质上和变量没什么区别
# func(test)
# print(test)# 思考:能否在func中调用传入的函数呢?
def func(xxxx):print(xxxx) # 0x00000235281FD310# print(id(xxxx))# 调用传入的函数:函数名()xxxx()# 调用func函数,传入函数名
func(test)

总结

当把函数名直接传递时,若要查看函数的效果,需要在函数内给参数添加()括号进行调用; 把函数名当作参数进行传递,可以应用于闭包和装饰器中。 

闭包

闭包的作用

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

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

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

代码案例 

例如,一起来完成:

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

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

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

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

# 例如,一起来完成:
# (1)此处来了解闭包的作用:保存函数内的变量;
# (2)定义一个有返回值的函数;
# (3)然后,调用函数并使用变量存储返回值结果;
# (4)在变量基础上,进行重复累加数值,观察效果。#1.
# 好的#2.
def show():a = 10return a#3.x = show()
print(x) # 10
#4.
print(x + 1) # 11
print(x + 2) # 12
print(x + 3) # 13
print(x + 4) # 14

总结

闭包可以用来保存函数内的变量值,而变量不会随着调用函数结束而销毁

使用闭包

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

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

定义闭包语法:

调用闭包语法:

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

说明:

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

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

代码案例

例如,一起来完成:

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

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

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

# 例如,一起来完成:
# (1)定义一个用于求和的闭包;
# (2)其中,外部函数有参数x,内部函数有参数y;;
# (3)然后调用并求解两数之和及输出,观察效果。#1.
def outer(x):def inner(y):# pass# 有引用,待会儿实现result = x + yprint(result)return inner#2.好的#3.
xxxx = outer(3) # xxxx 纪录的就是inner的内存地址值,也就是一个函数
# 调用xxxx函数(inner)
xxxx(4)

总结 

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

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

nonlocal关键字

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

nonlocal关键字语法:

说明:

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

代码案例

例如,一起来完成:

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

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

# 例如,一起来完成:
# (1)编写一个闭包,并让内部函数去修改外部函数内的变量a = 100;
# (2)记得使用【nonlocal 变量名】提前声明,并观察效果。#1.
def outer(x):def inner():# 如果我需要修改x的值呢?# 这么写,会报错。局部变量不让修改,如果硬是要修改,怎么办?nonlocal xx = x + 100print(x)return inner#2.
if __name__ == '__main__':xxxx = outer(3)xxxx()# 结论:nonlocal:在内部函数修改外部函数变量时使用。

总结

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

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

装饰器

装饰器入门:语法糖

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

定义装饰器:

使用装饰器的标准语法:

要构成装饰器,需要满足4个条件:

代码案例

例如,一起来完成:

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

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

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

# 例如,一起来完成:
# (1)我们知道,发表评论前是需要先登录的;
# (2)接着,先定义有发表评论的功能函数;
# (3)使用语法糖方式,在不改变原有函数的基础上,提示用户要先登录~。#1.
# b站。#2.# 写一个装饰器:和闭包的区别就是传入的是一个函数,闭包传入的是一个变量。
def outer(func):def inner():# passflag = True  # 说明有登录# flag = False  # 说明没有登录if not flag:print("实现登录的逻辑........最终登录成功...")# 调用原有函数func()return inner@outer
def comment():# #实现登录逻辑# flag = True # 说明有登录# if not flag:#     print("实现登录的逻辑........最终登录成功...")# 不能这么做,因为这么写,这个comment函数太冗余了。功能太复杂了,不方便维护。# 每个函数一定要功能单一。做一件事。# 软件开发原则:对修改关闭,对扩展开放。# todo 问题:不能修改comment函数的功能,并且还需要实现给用户提示。# 装饰器来实现。# 实现评论逻辑print("用户的评论实现逻辑....最终评论成功....")#3.测试代码
if __name__ == '__main__':# 这里虽然代码没有问题,但是不符合现实逻辑:必须先登录再评论。# comment()# 传统方式调用# xxxx = outer(comment)# xxxx()# 语法糖的调用:@外层函数名comment()

总结

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

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

装饰器入门:传统方式

定义装饰器:

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

代码案例

例如,一起来完成:

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

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

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

(4)了解装饰器的执行流程。[查看]

# 例如,一起来完成:
# (1)我们知道,发表评论前是需要先登录的;
# (2)接着,先定义有发表评论的功能函数;
# (3)使用传统方式,在不改变原有函数的基础上,提示用户要先登录~;
# (4)了解装饰器的执行流程。[查看]#1.好的#2.
def comment():print('用户评论的功能...')#3.
def outer(func):def inner():flag = Falseif flag:print("提示用户登录...")func()return inner
#4.if __name__ == '__main__':# 调用外层函数xxxx = outer(comment)# 调用内层函数xxxx()

总结

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

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

函数分类

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

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

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

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

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

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

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

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

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

总结

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

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

装饰无参无返回值的函数

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

代码案例

例如,一起来完成:

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

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

# 例如,一起来完成:
# (1)在给无参无返回值的原有函数求和计算结果之前;
# (2)添加一个友好提示(注意:不能改变源码):正在努力计算中...。#1.
import timedef add():x = 1y = 2print(x + y)
#2.
def outer(func):def inner():# pass# 功能增强print("正在努力计算中...")time.sleep(1)# 调用原函数# add()func()return inner
if __name__ == '__main__':# add()xxx = outer(add)xxx()# 结论:待装饰的函数参数和返回值和内层函数是一样的。

 总结

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

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

装饰有参无返回值的函数

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

代码案例

例如,一起来完成:

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

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

# 例如,一起来完成:
# (1)在给有参无返回值的原有函数求和计算结果之前;
# (2)添加一个友好提示(注意:不能改变源码):正在努力计算中...。#1.
import timedef add(x,y):print(x + y)def outer(func):def inner(x,y):print("正在努力计算中...")time.sleep(1)func(x,y)return inner#2.
if __name__ == '__main__':xxx = outer(add)xxx(1,2)

 总结

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

装饰无参有返回值的函数

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

代码案例

例如,一起来完成:

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

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

# 例如,一起来完成:
# (1)在给无参有返回值的原有函数求和计算结果之前;
# (2)添加一个友好提示(注意:不能改变源码):正在努力计算中...。#1.
import timedef add():return 1 + 2def outer(func):def inner():print("正在努力计算中...")time.sleep(1)return func()return inner#2.
if __name__ == '__main__':xxx = outer(add)print(xxx())

总结

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

装饰有参有返回值的函数

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

代码案例

例如,一起来完成:

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

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

# 例如,一起来完成:
# (1)在给有参有返回值的原有函数求和计算结果之前;
# (2)添加一个友好提示(注意:不能改变源码):正在努力计算中...。#1.
import timedef add(x,y):return x + ydef outer(func):def inner(x,y):print("正在努力计算中...")time.sleep(1)return func(x,y)return inner#2.
if __name__ == '__main__':# 传统调用方式# xxx = outer(add)# print(xxx(1, 2))# 高级写法print(outer(add)(1, 2))

总结

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Github 正常访问但是ping不同也无法进行git操作

Github 正常访问但是ping不同也无法进行git操作 原因&#xff1a;在git push 的时候 发现一直push不上去。 $ git push interview master ssh: connect to host github.com port 22: Connection timed out fatal: Could not read from remote repository.Please make sure yo…

基于Springboot + vue实现的夕阳红公寓管理系统

&#x1f942;(❁◡❁)您的点赞&#x1f44d;➕评论&#x1f4dd;➕收藏⭐是作者创作的最大动力&#x1f91e; &#x1f496;&#x1f4d5;&#x1f389;&#x1f525; 支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4dd;欢迎留言讨论 &#x1f525;&#x1f525;&…

gesp(C++二级)(16)洛谷:B4037:[GESP202409 二级] 小杨的 N 字矩阵

gesp(C++二级)(16)洛谷:B4037:[GESP202409 二级] 小杨的 N 字矩阵 题目描述 小杨想要构造一个 m m m \times m m

【brew安装失败】DNS 查询 raw.githubusercontent.com 返回的是 0.0.0.0

从你提供的 nslookup 输出看&#xff0c;DNS 查询 raw.githubusercontent.com 返回的是 0.0.0.0&#xff0c;这通常意味着无法解析该域名或该域名被某些 DNS 屏蔽了。这种情况通常有几个可能的原因&#xff1a; 可能的原因和解决方法 本地 DNS 问题&#xff1a; 有可能是你的本…

C++ 【回调函数】详解与代码解读

在现代软件开发中&#xff0c;回调函数是一个常用的工具&#xff0c;能够实现函数调用的延迟绑定&#xff0c;广泛应用于事件驱动、异步操作以及模块解耦等场景。本文将从基础概念、分类、实现方式到代码示例&#xff0c;全面讲解 C 回调函数的实现和应用。 什么是回调函数&…

嵌入式开发之使用 FileZilla 在 Windows 和 Ubuntu 之间传文件

01-FileZilla简介 FileZilla 是一个常用的文件传输工具&#xff0c;它支持多种文件传输协议&#xff0c;包括以下主要协议&#xff1a; FTP (File Transfer Protocol) 这是 FileZilla 最基本支持的协议。FTP 是一种明文传输协议&#xff0c;不加密数据&#xff08;包括用户名和…

IDEA XML 文件 SQL 提示

首先连接到对应的数据库。Database 里面要填写对应的数据库名称 配置当前项目的 SQL 方言&#xff0c;例如我这里是 MySQL 数据库管理系统&#xff0c;那么就选择 MySQL 此时就有 SQL 语法、表名、字段名等提示信息了

SSA-Transformer拿捏!麻雀搜索算法优化-Transformer多特征分类预测/故障诊断

SSA-Transformer拿捏&#xff01;麻雀搜索算法优化-Transformer多特征分类预测/故障诊断 目录 SSA-Transformer拿捏&#xff01;麻雀搜索算法优化-Transformer多特征分类预测/故障诊断效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现SSA-Transformer麻雀搜索…

SimForge HSF 案例分享|复杂仿真应用定制——UAVSim无人机仿真APP(技术篇)

导读 「神工坊」核心技术——「SimForge HSF高性能数值模拟引擎」支持工程计算应用的快速开发、自动并行&#xff0c;以及多域耦合、AI求解加速&#xff0c;目前已实现航发整机数值模拟等多个系统级高保真数值模拟应用落地&#xff0c;支持10亿阶、100w核心量级的高效求解。其低…

bev and occupancy 3D视觉工坊

纯激光雷达的占据预测数据集制作 Allan 方差&#xff0c;零漂&#xff0c;把imu静止在一个地方&#xff0c;看它的偏差 评估指标 waymo

ts总结一下

ts基础应用 /*** 泛型工具类型*/ interface IProps {id: string;title: string;children: number[]; } type omita Omit<IProps, id | title>; const omitaA: omita {children: [1] }; type picka Pick<IProps, id | title>; const pickaA: picka {id: ,title…

【分布式文件存储系统Minio】2024.12保姆级教程

文章目录 1.介绍1.分布式文件系统2.基本概念 2.环境搭建1.访问网址2.账号密码都是minioadmin3.创建一个桶4.**Docker安装miniomc突破7天限制**1.拉取镜像2.运行容器3.进行配置1.格式2.具体配置 4.查看桶5.给桶开放权限 3.搭建minio模块1.创建一个oss模块1.在sun-common下创建2.…

“进制转换”公式大集合

咱们都知道十进制是“逢10进1 ”&#xff0c;同理&#xff0c;N进制就是 “逢N进1”。进制其实就这么简单。它的麻烦之处在于各种进制之间的转换。 一、十进制整数转N进制 1&#xff0e;十进制转二进制 除2取余法&#xff1a;连续除以2&#xff0c;直到商为0&#xff0c;逆序…

matlab-数字滤波器设计与实战

文章目录 数字滤波器设计FIR 滤波器设计IIR 滤波器设计巴特沃斯滤波器切比雪夫 I 型滤波器切比雪夫II型椭圆滤波器线性相位与非线性相位零相位响应数字滤波器实战数字滤波器产生延迟的主要原因补偿滤波引入的延迟补偿常量滤波器延迟补偿与频率有关的延迟从信号中除去不需要的频…

python参数传递不可变对象含可变子对象

当传递不可变对象时。不可变对象里面包含的子对象是可变的。则方法内修改了这个可变对象&#xff0c;源对象也发生了变化。 a (10, 20, [5, 6]) print("a", id(a))def test01(m):print("m", id(m))m[2][0] 888print("修改m后m的值为{}".forma…

Midjourney技术浅析(八):交互与反馈

Midjourney 的用户交互与反馈通过用户输入&#xff08;User Input&#xff09;和用户反馈&#xff08;User Feedback&#xff09;机制&#xff0c;不断优化和改进图像生成的质量和用户满意度。 一、用户交互与反馈模块概述 用户交互与反馈模块的主要功能包括&#xff1a; 1.…

TB1801D 线性驱动 LED 恒流芯片

1、产品概述 TB1801D是一款专为12V灯珠设计的汽车灯专用的低压差恒流芯片&#xff0c;输出电流恒流精度≤3&#xff05;&#xff0c;外围结构简单。TB1801D 内置 130℃过温保护电路&#xff0c;可在各种散热条件下将 LED 灯珠温度控制在 140℃以内。TB1801D 内置 100V 的功率 M…

C# OpenCV机器视觉:凸包检测

在一个看似平常却又暗藏玄机的午后&#xff0c;阿强正悠闲地坐在实验室里&#xff0c;翘着二郎腿&#xff0c;哼着小曲儿&#xff0c;美滋滋地品尝着手中那杯热气腾腾的咖啡&#xff0c;仿佛整个世界都与他无关。突然&#xff0c;实验室的门 “砰” 的一声被撞开&#xff0c;小…

设计模式之访问者模式:一楼千面 各有玄机

~犬&#x1f4f0;余~ “我欲贱而贵&#xff0c;愚而智&#xff0c;贫而富&#xff0c;可乎&#xff1f; 曰&#xff1a;其唯学乎” 一、访问者模式概述 \quad 江湖中有一个传说&#xff1a;在遥远的东方&#xff0c;有一座神秘的玉楼。每当武林中人来访&#xff0c;楼中的各个房…

从0到机器视觉工程师(二):封装调用静态库和动态库

目录 静态库 编写静态库 使用静态库 方案一 方案二 动态库 编写动态库 使用动态库 方案一 方案二 方案三 总结 静态库 静态库是在编译时将库的代码合并到最终可执行程序中的库。静态库的优势是在编译时将所有代码包含在程序中&#xff0c;可以使程序独立运行&…