一、多重继承一个类为什么可以有多个爸爸上一篇文章我们学习了继承。例如class Animal: def eat(self): print(动物都会吃东西)创建狗类class Dog(Animal): pass这时候dog Dog() dog.eat()输出动物都会吃东西是不是很简单Dog 继承了 Animal所以自动拥有了eat()方法这种继承方式叫做单继承也就是说一个子类只有一个父类。关系可以画成这样Animal │ │ Dog1.1 那如果一个类想同时拥有两种能力怎么办来看一个例子 假设现在开发一款游戏游戏里有很多角色有的角色会跑class Run: def run(self): print(我会跑)有的角色会飞class Fly: def fly(self): print(我会飞)现在我们想创建一个超级英雄他既会跑又会飞怎么办如果按照以前的方法是不是得重新写一遍class SuperMan: def run(self): print(我会跑) def fly(self): print(我会飞)当然可以。但是这样做有一个问题如果以后会游泳 会隐身 会发射激光 会瞬移每增加一种能力了都得重新复制代码。程序员最讨厌的就是复制、粘贴、复制、粘贴……因为复制的时候很开心后面改代码的时候就会发现哪里都要改非常痛苦1.2 Python 提供了多重继承Python 可以同时继承多个父类。例如class Run: def run(self): print(我会跑) class Fly: def fly(self): print(我会飞) class SuperMan(Run, Fly): pass创建对象hero SuperMan() hero.run() hero.fly()输出我会跑 我会飞有没有发现SuperMan并没有写run() fly()但是却可以直接调用因为它同时继承了RunFly所以两个类的方法都拥有了。1.3 为什么叫多重继承因为以前一个爸爸 │ ▼ 儿子现在变成了Run Fly │ │ \ / \ / SuperMan一个类可以同时学习多个父类的能力这就是多重继承。1.4 多重继承什么时候会用到举一个现实中的例子假设开发一个机器人。机器人可以说话class Speak:可以跳舞class Dance:可以拍照class Camera:如果需要会说话还能拍照 的机器人。直接class Robot(Speak, Camera):是不是比重新写一遍方便得多所以多重继承最大的作用就是复用代码。1.5 多重继承有没有缺点当然有假设两个父类都有同一个方法class A: def hello(self): print(A)class B: def hello(self): print(B)子类class C(A, B): pass执行c C() c.hello()输出A为什么不是 B因为 Python 有自己的查找顺序它会按照一定规则寻找方法。对于初学者来说不用去死记这个规则只需要知道如果多个父类有同名方法Python 会按照继承顺序查找。所以实际开发中不要随便使用多重继承否则后期自己都会绕晕。1.6 Python 为什么还能支持多重继承很多编程语言例如 Java并不支持类的多重继承。为什么因为继承关系太复杂容易产生歧义。Python 之所以支持是因为它设计了一套完整的方法查找规则MRO不过这属于比较深入的知识。对于初学者来说暂时知道Python 可以支持多个父类即可。以后学习框架的时候自然会接触到。本节小结多重继承就是一个类同时继承多个父类。它最大的优点✅ 复用代码。✅ 一个类拥有多个能力。但也不要滥用因为继承关系太复杂维护起来会越来越困难。对于初学者来说记住一句话能用单继承解决就不要为了炫技写多重继承。二、定制类让自己的对象变得和 Python 内置对象一样聪明不知道大家有没有想过一个问题。为什么列表可以这样写nums [1, 2, 3] print(len(nums))字符串也可以print(len(Python))甚至字典data {name: 张三} print(len(data))为什么它们都支持len()再比如打印对象print(nums)输出[1, 2, 3]为什么打印自己写的类print(student)却变成__main__.Student object at 0x000001F4...这到底是为什么答案就是Python 内置对象实现了很多特殊方法。如果我们自己也实现这些方法自己的对象也会拥有这些能力。这就是定制类。简单来说按照 Python 的规则给自己的类增加特殊能力。2.1 第一个魔法方法__str__()先来看一个例子。class Student: def __init__(self, name): self.name name创建对象s Student(张三) print(s)输出__main__.Student object at 0x000001F4...很多小伙伴第一次看到这里都会说这是什么乱码其实这不是乱码这是 Python 默认打印出来的对象信息。对于程序来说没有问题但是对于人来说几乎没有任何意义。2.2 怎么让它变好看只需要重写__str__()例如class Student: def __init__(self, name): self.name name def __str__(self): return f学生姓名{self.name}再次打印s Student(张三) print(s)输出学生姓名张三是不是一下舒服多了以后只要print(对象)Python 就会自动调用__str__()2.3 __repr__()又是什么很多同学会发现网上还有__repr__()它和__str__()长得几乎一样有什么区别简单来说__str__()给用户看的。__repr__()给程序员看的。对于初学者来说很多时候可以直接让__repr__ __str__这样两个效果一样等以后接触大型项目再深入学习即可。2.3 __len__()让对象支持 len()大家都知道len([1,2,3])输出3其实Python 背后调用的是__len__()所以我们自己的对象也可以。class Student: def __len__(self): return 10创建对象s Student() print(len(s))输出10是不是很神奇以后只要别人调用len(对象)Python 就会自动执行__len__()2.4 __call__()对象也能像函数一样调用函数print()可以直接print()但是对象呢正常情况下s()会报错如果实现class Student: def __call__(self): print(对象被调用了)那么s Student() s()输出对象被调用了是不是很神奇对象居然可以像函数一样调用。2.5 定制类到底是在定制什么很多小白看到这里可能会觉得魔法方法越来越多。其实不用紧张它们本质上都是同一个思想告诉 Python如果别人这样操作我的对象你应该执行什么代码。例如print(对象)→ 调用__str__()len(对象)→ 调用__len__()对象()→ 调用__call__()这就是所谓的定制类你不是在学习一堆新的语法而是在学习如何让自己的对象拥有和 Python 内置对象一样的能力。本节小结定制类并不是一个新的类而是通过实现一些魔法方法让自己的对象支持更多操作。本节我们认识了几个最常用的方法魔法方法作用__str__()自定义打印对象时显示的内容__repr__()对象的开发者表示形式__len__()让对象支持len()__call__()让对象可以像函数一样调用对于初学者来说不需要把所有魔法方法都记住记住一句话就够了Python 很多看似神奇的功能背后其实都是这些魔法方法在默默工作。三、使用枚举类固定的数据别再用字符串乱写了相信大家平时写代码的时候经常会这样定义一些数据。例如表示性别gender 男或者gender 女看起来没有任何问题。再比如表示订单状态status 待付款付款之后status 已付款发货以后status 已发货最后status 已完成很多刚学习 Python 的同学都会这样写。因为简单直接容易理解。但是如果项目越来越大就会慢慢出现问题。3.1 字符串真的安全吗来看一个例子假设我们判断订单是否已经付款。status 已付款 if status 已付款: print(开始发货)没有问题但是如果某一天你不小心写成了status 以付款注意这里不是已付款而是以付款只差了一个字程序不会报错但是if status 已付款:条件永远不成立程序也不会提醒你。因为对于 Python 来说它只是一个普通字符串是真是假它不知道。3.2 如果一个项目有几百个字符串呢例如订单状态待付款 已付款 已发货 运输中 已签收 已完成如果每个人都自己写可能出现已付款 付款成功 已经付款 支付成功虽然表达的是同一个意思但是程序根本不知道这样就很容易产生 Bug。怎么办Python 提供了一个更加规范的方法枚举类Enum3.3 什么是枚举类一句话理解把固定不变的选项统一管理起来。例如一年有四季。交通灯只有三种颜色。一周只有七天。订单状态只有几个固定值。这些都是枚举。也就是说数量固定不会随便增加。3.4 创建一个枚举类Python 内置了Enum我们先导入from enum import Enum创建一个性别枚举from enum import Enum Gender Enum(Gender, (男, 女))现在我们不能随便写字符串了而是这样使用print(Gender.男)输出Gender.男如果获取值print(Gender.男.value)输出1再看print(Gender.女.value)输出2Python 会自动给每一个成员分配一个编号。当然编号一般不是重点。真正重要的是以后所有地方都统一使用Gender.男、Gender.女而不是随便写字符串。3.5 为什么要用枚举还是拿交通灯举例以前light 红灯现在Light.RED看到这里很多同学会觉得写字符串不是更简单吗刚开始确实如此但是如果一个项目有几十万行代码到处都是红灯 红色 停止 stop你根本不知道哪个才是正确的而枚举只有一种写法大家统一使用代码会更加规范。3.6 实际开发中哪些地方经常使用枚举枚举在项目中非常常见。例如订单状态待付款 已付款 已发货 已完成用户身份管理员 普通用户 游客星期星期一 星期二 …… 星期日交通灯红灯 黄灯 绿灯游戏角色职业战士 法师 射手 辅助这些固定的数据都非常适合使用枚举类。本节小结枚举类就是把固定的数据统一管理起来。它最大的优点就是不容易写错。代码更加规范。大型项目更容易维护。以后只要看到Enum你就可以理解成这是一个固定选项的集合。四、元类类到底是谁创建出来的终于来到第六章最后一个知识点也是很多人觉得最神秘的一个知识点。元类Metaclass网上很多教程一上来就是class MyMeta(type):相信很多同学看到这里直接关闭网页。因为根本不知道在写什么。其实元类并没有那么可怕。我们先回顾一下之前学习过的内容。4.1 第一步对象是谁创建的上一篇文章我们一直在写class Student: pass创建对象s Student()这里Student就是一个类而s就是对象所以对象是谁创建出来的答案当然是类。关系如下类 │ │ 创建 ▼ 对象这一点大家应该都能理解。4.2 第二步那类是谁创建出来的很多同学可能会回答Python 创建的。回答没错。但是更准确一点应该是元类创建的。也就是说实际上还有这样一层关系元类 │ │ 创建 ▼ 类 │ │ 创建 ▼ 对象是不是一下子清楚多了4.3 元类可以理解成造类的工厂举个生活中的例子。假设一家汽车工厂它的流程是这样的第一步工程师设计图纸。第二步工厂根据图纸生产汽车。关系如下图纸 │ ▼ 汽车Python 也类似不过多了一层。元类 │ ▼ 类 │ ▼ 对象如果把对象比作汽车类就是汽车图纸。元类就是制造图纸的工厂。所以很多人说元类就是创建类的类。虽然听起来有点绕但是意思就是专门负责创建类。4.4 那我们什么时候会写元类答案是几乎不会。是不是有点意外事实上对于绝大多数 Python 开发者来说。工作几年可能都不会自己写一个元类。因为Python 已经帮我们做好了。例如当我们写class Student: pass实际上。Python 背后已经使用默认元类type帮我们创建好了这个类。所以我们平时根本感觉不到它的存在。4.5 那为什么还要学习元类因为很多优秀的 Python 框架都会使用元类。例如DjangoSQLAlchemyDjango ORM一些大型框架虽然你不会自己写但是以后阅读源码的时候经常会遇到metaclass如果不知道元类是什么就会一脸懵所以学习元类主要目的是知道它是什么。而不是必须会写。本节小结元类只有一句话需要记住对象是类创建的类是元类创建的。对于初学者来说知道这一点就已经足够了等以后学习框架源码再深入学习也不迟。全文总结恭喜你如果能够坚持看到这里说明你已经掌握了 Python 面向对象编程的大部分进阶知识。这次我们学习了六个重要知识点。__slots__限制对象可以拥有的属性让代码更加规范还能节省一定内存。property把方法变成属性既保证数据安全又让代码更加优雅。这是实际开发中使用频率非常高的一个知识点。多重继承一个类可以同时继承多个父类实现代码复用不过不要为了炫技而滥用。定制类通过实现魔法方法让自己的对象支持print()len()对象()等各种操作。让对象拥有和 Python 内置对象一样的能力。枚举类适合管理固定的数据。例如星期性别订单状态用户角色让代码更加规范减少因为字符串写错而导致的 Bug。元类知道即可记住一句话元类创建类类创建对象。以后学习 Django、Flask、ORM 等框架时你还会再次遇到它。不知不觉我们已经从最开始的变量、列表、字典一路学习到了 Python 面向对象编程的进阶内容。你可能会发现现在学的很多知识平时写几十行小程序未必都能用到。但它们存在的意义并不是为了让代码变得更复杂而是为了让程序在规模越来越大、参与开发的人越来越多时依然能够保持清晰、规范、易维护。学习编程就像搭积木基础知识决定你能不能把积木搭起来而这些进阶知识则决定你的积木能搭多高、搭得稳不稳。希望通过这一系列文章你不仅学会了 Python 的语法更能够逐渐理解 Python 的编程思想。当以后再次看到property、__slots__、Enum或者元类时不会再觉得陌生而是能够明白它们为什么会存在又该在什么场景下使用。