Python小练习:class使用中的一些技巧
作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/
本文主要介绍Python代码中@property、pass、@abc.abstractmethod、raise NotImplementedError、super().__init__、*args与**kwargs等所起的作用。
1. python_class.py
1 # -*- coding: utf-8 -*- 2 # Author:凯鲁嘎吉 Coral Gajic 3 # https://www.cnblogs.com/kailugaji/ 4 # python class使用中的一些技巧 5 ''' 6 部分代码参考于: 7 https://blog.csdn.net/weixin_42681866/article/details/83376484 8 https://blog.csdn.net/qq_40666620/article/details/105026716 9 ''' 10 import abc # 抽象类 11 from typing import Dict, List, Any, Union 12 13 14 class Person(object): 15 def __init__(self, name, age): 16 self.name = name 17 self.__age = 18 # 双下划线的age定义为私有变量 18 # 这里的成员属性__age需要与成员方法age()区分开 19 20 # python @property的使用 21 # property装饰器的作用:将方法变成属性调用 22 @property 23 def age(self): 24 return self.__age 25 # 方法加入@property后,这个方法相当于一个属性,该属性可以让用户进行使用,而且用户有没办法随意修改 26 27 @age.setter 28 def age(self, age): 29 if age < 18: 30 print('年龄必须大于18岁') 31 return 32 self.__age = age 33 return self.__age 34 35 # pass的使用 36 # pass的作用:代码的占位符 37 def Test_pass(self): 38 pass 39 # 现在这个方法我还没想好里面要写啥东西,但又不能空着不写,所以用pass先占个位 40 41 # @abc.abstractmethod的使用 42 # @abc.abstractmethod的作用:实现抽象方法 43 # 基类不能实例化,子类实现了该抽象方法才能被实例化 44 @abc.abstractmethod 45 def Test_abstract(self, weight): 46 pass 47 48 # raise NotImplementedError的使用 49 # raise NotImplementedError的作用:如果子类不重写这个方法,就会抛出异常 50 def Test_Error(self): 51 raise NotImplementedError("没有重写Test_Error方法,因此抛出NotImplementedError异常!") 52 53 # 父类与子类的使用 54 # raise NotImplementedError的使用 55 # 当子类没有重写父类中的成员函数,然后子类对象调用该函数时,会提示这个错误! 56 # pass的使用 57 class PersonTwo(Person): # 继承于父类Person 58 # 对父类的Test_Error方法进行了重写 59 def Test_Error(self): 60 print('你好!\n' 61 '我重写了父类Person中的Test_Error方法!\n' 62 '因此没有抛出NotImplementedError异常!') 63 64 # 对父类的Test_pass方法进行了重写 65 def Test_pass(self): 66 print('我现在想好了,准备写这段话!') 67 68 # 对父类的Test_abstract方法进行了重写 69 def Test_abstract(self, weight): 70 print('体重:', weight) 71 72 # 没有重写Test_Error方法 73 class PersonThree(Person): 74 def Test_Error2(self): 75 print('Sorry!') 76 77 # super().__init__的使用 78 # super().__init__()作用:用来调用super class内的__init__构造方法 79 class PersonFour(Person): 80 def __init__(self, name, age, height): 81 super().__init__(name, age) 82 self.height = height 83 84 # *args与**kwargs的使用 85 # *args的作用:arguments,位置参数,在当传入的参数个数未知,且不需要知道参数名称时使用 86 # **kwargs的作用:keyword arguments,关键字参数,当传入的参数个数未知,但需要知道参数的名称时使用 87 def roster(para1, *args, **kwargs): 88 print('必选参数:', para1) 89 print('位置参数:', args) 90 print('位置参数求和结果:', sum(args)) 91 print("关键字参数数据类型:", type(kwargs)) 92 print('************花名册************') 93 count = 0 94 for key, value in kwargs.items(): 95 count = count +1 96 print('第%d个学生的姓名:' % count, key) 97 print('第%d个学生的信息:' % count) 98 for i, j in value.items(): 99 print(i, ':', j) 100 101 print('*args与**kwargs的用法:') 102 tuple = (1, 2, 3, 4) 103 dict1 = {"小明":{"年龄": 15, "身高": 165, "体重": 55}, 104 "小红":{"年龄": 18, "身高": 170, "体重": 60}, 105 "小王":{"年龄": 25, "身高": 180, "体重": 66}, 106 "小张":{"年龄": 30, "身高": 175, "体重": 58}} 107 roster('花名册', *tuple, **dict1) 108 109 print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~') 110 print('@property的用法:') 111 # 创建实例 112 xm = Person('小明', 20) 113 xm.name='小红' 114 xm.__age=5 115 print('姓名:', xm.name) # 按最新的来 116 print('---------------------') 117 print('情况1:') 118 print('年龄:%d岁' % xm.age) # 默认18岁,xm.__age改了,不关xm.age的事 119 # 使用@property装饰器来创建只读属性 120 # @property装饰器会将方法转换为相同名称的只读属性 121 # 可以与所定义的属性配合使用,这样可以防止属性被修改 122 print('---------------------') 123 print('情况2:') 124 xm.age = 10 125 print('年龄:%d岁' % xm.age) 126 print('---------------------') 127 print('情况3:') 128 xm.age=20 129 # 使用@property装饰器来修饰方法,使方法可以像属性一样访问 130 # 加了@property后,可以用调用属性的形式来调用方法,后面不需要加() 131 print('年龄:%d岁' % xm.age) 132 133 # pass的使用 134 print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~') 135 print('pass的用法:') 136 print('情况1:父类') 137 c = Person('小李', 28) 138 c.Test_pass() 139 print('---------------------') 140 print('情况2:PersonTwo子类') 141 d = PersonTwo('小李', 28) 142 d.Test_pass() 143 print('---------------------') 144 print('情况3:PersonThree子类') 145 e = PersonThree('小王', 35) 146 e.Test_pass() 147 148 # @abc.abstractmethod的使用 149 print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~') 150 print('@abc.abstractmethod的用法:') 151 g = PersonTwo('小李', 28) 152 g.Test_abstract(55) # 实例化,体重55KG 153 154 # super().__init__的使用 155 print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~') 156 print('super().__init__的用法:') 157 f = PersonFour('小张', 26, 170) 158 print('姓名:', f.name) 159 print('年龄:', f.age) # 不是26,还是默认的18 160 print('身高:', f.height) 161 162 # raise NotImplementedError的使用 163 print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~') 164 print('raise NotImplementedError的用法:') 165 print('情况1:') 166 a = PersonTwo('小李', 28) 167 a.Test_Error() 168 print('---------------------') 169 print('情况2:') 170 b = PersonThree('小王', 35) 171 b.Test_Error2() 172 b.Test_Error() # 删除这行不报错
2. 结果
D:\ProgramData\Anaconda3\python.exe "D:/Python code/exercise/python_class.py" *args与**kwargs的用法: 必选参数: 花名册 位置参数: (1, 2, 3, 4) 位置参数求和结果: 10 关键字参数数据类型: <class 'dict'> ************花名册************ 第1个学生的姓名: 小明 第1个学生的信息: 年龄 : 15 身高 : 165 体重 : 55 第2个学生的姓名: 小红 第2个学生的信息: 年龄 : 18 身高 : 170 体重 : 60 第3个学生的姓名: 小王 第3个学生的信息: 年龄 : 25 身高 : 180 体重 : 66 第4个学生的姓名: 小张 第4个学生的信息: 年龄 : 30 身高 : 175 体重 : 58 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @property的用法: 姓名: 小红 --------------------- 情况1: 年龄:18岁 --------------------- 情况2: 年龄必须大于18岁 年龄:18岁 --------------------- 情况3: 年龄:20岁 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ pass的用法: 情况1:父类 --------------------- 情况2:PersonTwo子类 我现在想好了,准备写这段话! --------------------- 情况3:PersonThree子类 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @abc.abstractmethod的用法: 体重: 55 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ super().__init__的用法: 姓名: 小张 年龄: 18 身高: 170 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ raise NotImplementedError的用法: 情况1: 你好! 我重写了父类Person中的Test_Error方法! 因此没有抛出NotImplementedError异常! --------------------- 情况2: Sorry! Traceback (most recent call last): File "D:/Python code/exercise/python_class.py", line 172, in <module> b.Test_Error() # 删除这行不报错 File "D:/Python code/exercise/python_class.py", line 51, in Test_Error raise NotImplementedError("没有重写Test_Error方法,因此抛出NotImplementedError异常!") NotImplementedError: 没有重写Test_Error方法,因此抛出NotImplementedError异常! Process finished with exit code 1
注意:代码中最后一行是为了说明raise NotImplementedError的作用,把最后一行 b.Test_Error() 删掉后,程序就不会报错了。
补充:下面这个程序用于判断输入的类名称是否是子类,是,则返回该子类。
1 # -*- coding: utf-8 -*- 2 # Author:凯鲁嘎吉 Coral Gajic 3 # https://www.cnblogs.com/kailugaji/ 4 # 判断输入的类名称是否是子类,是,则返回该子类 5 class BaseClass: 6 def __init__(self): 7 self.value = 0 8 9 def set_value(self, value): 10 self.value = value 11 12 def get_value(self): 13 return self.value 14 15 class SubClass(BaseClass): 16 def __init__(self): 17 super().__init__() 18 self.value = 1 19 20 def set_value(self, value): 21 super().set_value(value) 22 self.value += 1 23 24 def get_value(self): 25 return self.value 26 27 # 这段代码定义了一个名为get_subclass的函数,它接受两个参数:base_class表示基类,class_name表示要查找的子类名称。 28 # 该函数使用递归的方式来查找基类的所有子类,并返回其中名称与子类名称相同的子类。如果找不到名称相同的子类,则返回None。 29 # 在函数体内,首先使用__subclasses__方法获取基类的所有子类,然后使用get_subclass函数递归地查找每个子类。 30 # 如果找到了名称与子类名称相同的子类,则返回该子类。如果没有找到,则返回None。 31 # 请注意,这段代码假设基类中的所有子类都是可枚举的,即它们都有一个唯一的名称。 32 # 如果基类中的子类是可变的,则需要使用其他方法来查找子类。 33 def get_subclass(base_class, class_name): 34 for c in base_class.__subclasses__(): 35 if c.__name__ == class_name: 36 return c 37 for c in base_class.__subclasses__(): 38 temp_c = get_subclass(c, class_name) 39 if temp_c is not None: 40 return temp_c 41 return None 42 43 subclass = SubClass() 44 result = get_subclass(BaseClass, "SubClass") 45 if result is not None: 46 print(f"SubClass {subclass.value} is a subclass of BaseClass") 47 else: 48 print(f"SubClass {subclass.value} is not a subclass of BaseClass")
结果:
SubClass 1 is a subclass of BaseClass
可以看到,SubClass属于基类BaseClass中的子类,因此调用子类,返回值1。