Contents

  • 1. 什么是类和实例
  • 2. 类的实例方法、类方法和静态方法
  • 3. MRO是什么,描述其查找顺序
  • 4. Mixin是什么,描述其应用场景
  • 5. References

1. 什么是类和实例

要求: 并说明类与实例之间的关系

是Python通过定义属性和方法,对待求解问题的概括抽象。其中属性用于描述该类的特性,方法用于描述该类支持的对属性的操作集合。在Python中,类是一种用户自定义数据类型,通过类,可以实现对现实问题的抽象和概括以及归纳。

实例是类的具体实现,是对类的填充和丰富,是类的具象化表现。

下面用实例代码说明类以及类的实例对象之间的关系。

具体如下面的代码,定义了Dog类,同时创建了该类的实例对象——名叫Miracle的狗狗。

class Dog:def __init__(self, name, age, gender):self.name = nameself.age = ageself.gender = genderdef __repr__(self):return 'Dog Name: {}\nDog Age: {}\nDog Gender: {}'.format(self.name,self.age,self.gender)dog_miracle = Dog('Miracle', 5, 'Male')print(dog_miracle)

上述代码中,class Dog就是类的定义语句,第15行代码创建了该类的实例对象——一个名叫Miracle、年龄为5岁的雄性狗狗。

2. 类的实例方法、类方法和静态方法

**要求:**举例说明如何定义类的实例方法、类方法和静态方法,并总结它们的应用场景。

类的实例方法就是在类中定义的普通函数,其第一个参数必须是该类的实例对象,在Python中通常用self表示这个参数,实例方法通常由该类的实例对象调用,调用过程中,会自动将该实例对象作为参数传递给self

类方法也是在类中定义的,但是不同于普通的实例方法,它的第一个参数使用该类自身作为参数,且该方法在定义的时候需要使用classmethod装饰器进行装饰。通常既可以由该类直接调用,也可以通过该类的实例对象调用,调用的时候,会自动将该类自身作为参数产地给类方法。

静态方法也是在类中定义的,这个方法在定义的时候需要使用staticmethod装饰器进行装饰,既可以通过该类的实例对象调用,也可以通过该类直接调用。在调用的过程中,并不会像实例方法和类方法那样隐式传递参数。

类的实例方法,通常用于操作实例对象的属性,所以其第一个参数通常为self;而类方法的第一个参数是该类自身cls,在实例方法中可以构建该类的实例对象,即通常用于工厂方法(工厂方法返回该类的实例对象)。

而静态方法在调用的时候,既不会被隐式传递self参数也不会被传递cls参数,该方法通常表示其只是适用于类本身,并不适合于该类创建的实例对象,所以静态方法通常用于该类中贝类调用的应用函数。

下面通过具体的代码说明上述三种方法的作用以及定义和调用方式。

下面的代码中定义了一个学生分数类StudentScore,在其中定义了一个类方法build_obj,除此之外,还定义了一个用于检查成绩合法性的静态方法valid_score。在build_obj调用valid_score检查输入的分数是否合法,如果合法,则在build_obj中生成该类的实例对象并返回该对象。如果检查的分数不合法,则抛出异常,build_obj函数会处理异常。具体代码如下所示:

"""
在类中分别实现普通实例方法、类方法以及静态方法
"""class StudentScore:def __init__(self, name, course, score):self.__name = nameself.__course = courseself.__score = scoredef __repr__(self):return 'StudentScore({}\'s {} score is {})'.format(self.__name,self.__course,self.__score)def show_score(self):print(self)@classmethoddef build_obj(cls, csv_str):try:vld_csv_str = cls.valid_score(csv_str)except:print('{} is invalid'.format(csv_str))else:return cls(*vld_csv_str)@staticmethoddef valid_score(csv_str):nm, crs, scr = csv_str.split(',')scr = int(scr)if scr < 0 or scr > 100:raise ValueError('The score {} is invalid'.format(scr))else:return nm, crs, scrif __name__ == '__main__':student_score_lst = ['张三,物理,80','李四,物理,70','王五,化学,90','赵六,化学,85','Error,英语,101','Exception,英语,-5']result_lst = []for rec in student_score_lst:result = StudentScore.build_obj(rec)if result is not None:result_lst.append(result)print(result_lst)

上述代码的输出结果如下所示:

Error,英语,101 is invalid
Exception,英语,-5 is invalid
[StudentScore(张三's 物理 score is 80), StudentScore(李四's 物理 score is 70), StudentScore(王五's 化学 score is 90), StudentScore(赵六's 化学 score is 85)]Process finished with exit code 0

上述的结果列表中并不包含无效的成绩结果。实现了该类的作用。

3. MRO是什么,描述其查找顺序

所谓MRO(Method Resolution Order),是指在多重继承环境中,属性或者方法的解析顺序。当一个类继承了多个类的时候,此时类中方法以及属性的解析顺序就遵照MRO规则。MRO采用了C3 Linearization算法构建父类列表,Python-2.3开始引入这个方法。关于该方法的概括如下:

Wikipedia does a great job explaining the algorithm. It can be reduced to the following steps:

  1. Linearization (i.e. resolution order) is a class itself and a merge of the linearizations of its parents and a list of the parents itself
  2. Linearization of the class with no parents equals to the class itself.
  3. Merge process is done by selecting the first head of the lists which does not appear in the tail of any of the lists. Where head is the first element of the list, and tail is all but first elements of the list. The heads are repeatedly selected and added to the resulting MRO until all the lists are exhausted.
  4. If a head cannot be selected while not all the lists are exhausted merge is impossible to compute due to inconsistent orderings of dependencies in the inheritance hierarchy and no linearization of the original class exists.

以下面的复杂继承关系图谱作为示例,说明该算法是如何构建出MRO继承顺序的。
以K1这个类的继承关系作为演示,此时K1的MRO继承关系构建过程如下所示:

// first, find the linearizations of K1's parents, L(A), L(B), and L(C),
// and merge them with the parent list [A, B, C]
L(K1) := [K1] + merge(L(A), L(B), L(C), [A, B, C])
// class A is a good candidate for the first merge step, because it only
// appears as the head of the first and last lists= [K1] + merge([A, O], [B, O], [C, O], [A, B, C])
// class O is not a good candidate for the next merge step, because it also
// appears in the tails of list 2 and 3; but class B is a good candidate= [K1, A] + merge([O], [B, O], [C, O], [B, C])
// class C is a good candidate; class O still appears in the tail of list 3= [K1, A, B] + merge([O], [O], [C, O], [C])
// finally, class O is a valid candidate, which also exhausts all remaining lists= [K1, A, B, C] + merge([O], [O], [O])= [K1, A, B, C, O]
  1. 首先,按照线性关系查找K1的父类,找到L(A), L(B), L(C)这三个类,将这三个类合并为父类列表:[A, B, C]。此时K1的继承关系为:L(K1) := [K1] + merge(L(A), L(B), L(C), [A, B, C])
  2. 此时,类A是第一步合并的比较好的候选类,因为它是第一个和最后一个列表的开头,此时的继承关系为:L(K1) := [K1] + merge([A, O], [B, O], [C, O], [A, B, C])
  3. 此时类O对于下一次合并来说,并不是一个很好的候选类,因为它出现在第2个和第3个列表的末尾。不过类B倒是一个不错的候选项,此时的继承关系为:L(K1) := [K1, A] + merge([O], [B, O], [C, O], [B, C])
  4. 此时,类C是一个不错的候选项,而类O仍然出现在第3个列表的末尾,所以此时的继承关系为:L(K1) := [K1, A, B] + merge([O], [O], [C, O], [C])
  5. 最后,类O是一个有效的候选项,因为此时已经遍历完成了父类列表,此时的继承关系为:L(K1) := [K1, A, B, C] + merge([O], [O], [O])。由于这一步的合并操作中,只剩下类O,所以直接将类O加入到父类列表中即可。得到K1类最终的父类列表:L(K1) := [K1, A, B, C, O]

所以,上图中,最终K1类的MRO解析顺序为:K1 -> A -> B -> C -> O

下面通过代码,模拟上面的继承关系,具体如下所示:

定义三个类:A,B,C,使用这三个类作为父类,派生出类K1。具体代码如下所示:

"""
定义三个类:A,B,C,使用这三个类作为父类,派生出类K1。
"""class A:a = 1def __init__(self):self.x = 1def __repr__(self):return 'in class A'class B:b = 2def __init__(self):self.y = 2def __repr__(self):return 'in class B'class C:c = 3def __init__(self):self.z = 3def __repr__(self):return 'in class C'class K1(A, B, C):       # (<class '__main__.K1'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>)
# class K1(A, C, B):       # [<class '__main__.K1'>, <class '__main__.A'>, <class '__main__.C'>, <class '__main__.B'>, <class 'object'>]
# class K1(C, B, A):       # (<class '__main__.K1'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)passk1 = K1()
print(k1.__dict__)
print(K1.__mro__)
print(k1.x)
# print(k1.y)       # K1类继承了A的方法,所以A中的__init__()方法覆盖了此后父类中的__init__()方法,就会造成这个类中只有x属性,而没有其他属性
# print(k1.z)       # K1类继承了A的方法,所以A中的__init__()方法覆盖了此后父类中的__init__()方法,就会造成这个类中只有x属性,而没有其他属性
print(k1)
print(K1.__dict__)
print(k1.a, k1.b, k1.c)     # 父类的类属性可以被子类继承,但是父类中定义的实例属性是无法全部被子类继承的,只会继承第一实现了__init__()方法的父类中的实例属性。
# print(K1.mro())    # 与K1.__mro__的输出结果相同
# print(k1.__mro__)  # 实例对象中没有这个属性
# print(k1.mro())    # 实例对象中同样没有这个方法

第33-35行中,证实了父类列表中的出现顺序,会决定MRO中的解析顺序,MRO的父类解析顺序与K1类的父类继承列表中指定的顺序是保持一致的。

上述代码的执行结果如下所示:

{'x': 1}
(<class '__main__.K1'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>)
1
in class A
{'__module__': '__main__', '__doc__': None}
1 2 3Process finished with exit code 0

多重继承会导致事情变得很复杂,很难弄清确切的继承路径,所以在实际开发过程中,除非必要,否则应该尽量避免使用过多的多重继承。

4. Mixin是什么,描述其应用场景

Mixin类与装饰器技术通常是以非侵入目标类定义的方式,增强目标类的功能。目标类通过继承Mixin类,实现自我功能的丰富,但是又无需对目标类的定义做出较大修改。

由于Mixin类并没有定义新的类型,只是用于目标类的父类,完善目标类的功能特性,所以通常并不会将Mixin类实例化,所以一般不会在Mixin类中定义__init__()方法。Mixin类通常也不单独使用,一般是作为目标类的父类而被使用。所以Mixin类的本质是通过多重继承将Mixin类中定义的功能注入到目标类中。Mixin类通常放在目标类继承列表中的第一个,以便在解析目标类的实例对象属性或者方法的时候,可以尽早得到相应的属性与方法。

接下来通过代码演示Mixin类的使用方式。

定义一个Person类,只提供一个名字属性,作为一个基本的数据类型抽象;另外定义一个Employee类,继承自Person类,其中除了包含名字属性之外,还定义了技能、亲属依赖关系等属性。另外,想要将Employee类的实例对象可以转化为字典打印,也需要使其能够支持转换为JSON格式打印输出。这两个功能明显不是Employee类逻辑上应该支持的功能,作为附加属性,可以考虑使用装饰器实现,但是此处使用Mixin类实现。

定义一个MixinDict类将Employee类的实例对象格式化为字典的形式打印;再定义一个MixinJson类将Employee类的实例对象转换为JSON格式输出。

具体代码如下所示:

"""
Mixin类的示例
"""import jsonclass Person:def __init__(self, name):self.name = nameclass MixinDict:def to_dict(self, dct):"""flat a dictbefore flat -> {'name': 'Joy', 'skills': 'Python Programming', 'dependents': {'wife': 'Jane', 'children': ['Alice', 'Bob', 'Justin']}}------------------------------after flat -> {'name': 'Joy', 'skills': 'Python Programming', 'wife': 'Jane', 'children': ['Alice', 'Bob', 'Justin']}:param dct::return:"""res_dict = {}temp_dct = {}for key, val in dct.items():if isinstance(val, dict):temp_dct = self.to_dict(val)else:res_dict[key] = valelse:res_dict.update(temp_dct)return res_dictclass MixinJson:def to_json(self, ori_dct):flat_dct = self.to_dict(ori_dct)return json.dumps(flat_dct)class Employee(MixinDict, MixinJson, Person):def __init__(self, name, skills, dependents):super().__init__(name)self.skills = skillsself.dependents = dependentsif __name__ == '__main__':# e1 = Employee(#     name = 'Joy',#     skills = ['Python Programming', 'Project Management'],#     dependents = {'wife': 'Jane', 'children': ['Alice', 'Bob', 'Canne']}# )e1 = Employee('Joy', 'Python Programming', {'wife': 'Jane', 'children': ['Alice', 'Bob', 'Justin']})print('original dict: ', e1.__dict__)print('-' * 30)e1_dct = e1.__dict__e1_flat_dct = e1.to_dict(e1_dct)print('flat dict: ', e1_flat_dct)print('=' * 30)e1_json = e1.to_json(e1_flat_dct)print('flat json: ', e1_json)

上述代码的执行结果如下所示:

original dict:  {'name': 'Joy', 'skills': 'Python Programming', 'dependents': {'wife': 'Jane', 'children': ['Alice', 'Bob', 'Justin']}}
------------------------------
flat dict:  {'name': 'Joy', 'skills': 'Python Programming', 'wife': 'Jane', 'children': ['Alice', 'Bob', 'Justin']}
==============================
flat json:  {"name": "Joy", "skills": "Python Programming", "wife": "Jane", "children": ["Alice", "Bob", "Justin"]}Process finished with exit code 0

上述代码就是通过MixinDict类以及MixinJson这两个Mixin类,增强了Employee这个类的功能。

5. References

[1]. Python’s @classmethod and @staticmethod Explained
[2]. Class method vs Static method in Python
[3]. The Python 2.3 Method Resolution Order
[4]. Method resolution order in Python Inheritance
[5]. Method Resolution Order (MRO) in new-style classes
[6]. Python Method Resolution Order and C3 linearization algorithm
[7]. Python mixin

Python中的类、实例以及方法,MRO继承解析顺序以及Mixin类相关推荐

  1. python创建类的实例方法-Python中动态创建类实例的方法

    简介 在Java中我们可以通过反射来根据类名创建类实例,那么在Python我们怎么实现类似功能呢? 其实在Python有一个builtin函数import,我们可以使用这个函数来在运行时动态加载一些模 ...

  2. Python中optionParser模块的使用方法实例教程

    Python中optionParser模块的使用方法实例教程 转载  2014-08-29   投稿:shichen2014    我要评论 这篇文章主要介绍了Python中optionParser模 ...

  3. Python中的静态类变量和方法

    问: 如何在 Python 中创建静态类变量或方法? 答1: huntsbot.com聚合了超过10+全球外包任务平台的外包需求,寻找外包任务与机会变的简单与高效. 在类定义内但不在方法内声明的变量是 ...

  4. 关于python中requests模块导入问题-python中requests模块的使用方法

    本文实例讲述了python中requests模块的使用方法.分享给大家供大家参考.具体分析如下: 在HTTP相关处理中使用python是不必要的麻烦,这包括urllib2模块以巨大的复杂性代价获取综合 ...

  5. python弹出警告框_selenium+webdriver+python 中警告框的处理方法

    在自动化测试过程中,经常会遇到弹出警告框的情况,如图所示: 在 WebDriver 中处理 JavaScript 所生成的 alert.confirm 以及 prompt 是很简单的.具体做法是使用 ...

  6. python中不被定义_一日一技:在Python中双下划线私有方法不能被调用的原理

    一日一技:在Python中双下划线私有方法不能被调用的原理 在使用Python编写面向对象的代码时,我们会常常使用"继承"这种开发方式.例如下面这一段代码: class Info: ...

  7. Python中私有变量和私有方法芳

    Python中要想定义的方法或者变量只能在类内部使用不被外部使用,可以在方法和变量前面加两个下划线,让其变为私有方法或私有变量.类外部可以通过 "_类名__私有属性(方法)名" 访 ...

  8. python文件处理seek()方法的参数是,在Python中操作文件之seek()方法的使用教程

    在Python中操作文件之seek()方法的使用教程 seek()方法在偏移设定该文件的当前位置.参数是可选的,默认为0,这意味着绝对的文件定位,它的值如果是1,这意味着寻求相对于当前位置,2表示相对 ...

  9. series 合并pandas_在python中pandas的series合并方法

    如下所示: In [3]: import pandas as pd In [4]: a = pd.Series([1,2,3]) In [5]: b = pd.Series([2,3,4]) In [ ...

最新文章

  1. vue后台增删改查_Vue 原生实现商城购物车增删改查
  2. Spring Boot中如何干掉过多的if else!
  3. nfs:server 172.168.1.22 not responding,still trying问题解决方法 平台为RealARM 210平台
  4. js plugin--headroom
  5. 【线上分享】WebRTC传输与服务质量
  6. “生而强悍” vivo iQOO官宣3月1日发布
  7. hdu 5094 Maze bfs
  8. android adb模拟点击,Android adb 模拟滑动 按键 点击事件(示例代码)
  9. iOS中__block 关键字的底层实现原理
  10. 现代语音信号处理之时域分析
  11. python处理地震sac数据_Fortran批量读取SAC地震数据文件,写SAC文件,并转换成SEGY...
  12. pthread 编程
  13. 【北交所周报】IPO上会5过5;四成个股实现上涨,硅烷科技涨56%,成单周涨幅最大个股;...
  14. image caption
  15. 如何打开管理员命令提示符窗口?
  16. Android中wifi管理器WifiManager使用方法
  17. 我对技术的态度是什么样的?
  18. 微信读书是看公众号文章的另一种选择
  19. 计算机远程控制相关考题,北邮远程计算机试题和答案.docx
  20. CPU VS GPU

热门文章

  1. LaTex论文排版 | (30) 三线表
  2. Box2D物理引擎入门
  3. [Unity3D]手机3D游戏开发:FPS射击游戏中瞄准镜CrossHair的制作
  4. 【QA】数学符号 word输入问题 在word里面怎么输入字母头顶上的那个小尖儿
  5. 5首2021一些流行好听歌曲的吉他和弦
  6. KeyBERT进行中文关键词提取
  7. win10亮度调节消失的解决方法
  8. 游戏项目框架(属性名+方法名)
  9. 水果店圈子:水果店开业前需要做的准备,水果店开业当天要注意哪些问题
  10. 凌晨3点不回家-现实版