理解Python3的面向对象编程
面向对象是相当于面向过程而言的, 面向过程语言是一种基于功能分析的, 以算法为中心的程序设计方法, 而面向对象是一种基于结构分析的, 以数据为中心的程序设计思想.
在面向对象语言中有一个很重要的东西, 叫做类.
面向对象有三大特性: 封装、继承、多态.
面向对象编程的四要素: 类、属性(变量或@property
装饰的函数)、方法(函数)、实例.
1. 类
一群有着相同属性和方法的对象的集合, 也可以具象化的理解为是一群有着相似特征的事物的集合; 用class
来声明.
抽象类
是一种特殊的类, 只能作为父类存在, 一旦对象化(或叫实例化)就会报错; 一般使用class Classname(metaclass=ABCMeta)
来声明.
类的继承
- 子类继承父类, 子类可以使用父类的属性和函数, 同时子类可以有自己独特的属性和函数;
- 子类如果没有初始化函数, 那么在生成对象的时候(实例化时), 将会继承父类的初始化函数;
- 子类如果有自己的初始化函数, 实例化时是不会自动调用父类的初始化函数的, 如果需要必须在子类的初始化函数中显示的调用父类的初始化函数(通过
super
来调用, 如果同时继承多个父类, 其他的就得用父类的名字来调用了); - 继承的优势是减少重复代码, 降低系统熵值(即复杂度).
类的封装
self.__attr = var
通过2个下划线这种方式来定义类的属性的时候, 要想改变类的属性只能通过类的成员函数来改变, 而不能通过实例.__attr=var
的方式来改变, 这种特性要叫做类的封装.
类的多态
子类继承父类的属性和方法, 子类既可以直接使用父类的方法, 又可以重写覆盖掉父类的方法, 表现出多种状态, 所以叫多态.
一般的用法: 子类重新定义父类的同名方法, 先做一些属性或一些其他东西的更改后, 再通过super
强制调用父类的方法.
2. 属性
分为实例属性和类属性, 属性可以是变量, 也可以是经过@property
装饰的函数.
实例属性
实例属性就是在类的初始化函数中定义的形如self.foo
的变量, 注意不是以self.
开头的变量不是实例属性, 而只是初始化函数中的普通变量而已;
调用方法: 类实例化后, instancename.foo
.
类属性
- 类属性就是在类中和函数在同一级别定义的变量, 是类的所有实例之间共享的变量;
- 类属性如果约定为是可变的, 就用小写命名, 如果约定为是不可变的, 即可理解为常量, 建议用全大写命名;
- 类属性的定义方法和普通变量的定义方法没有区别, 不能以
self.foo
的形式命名; - 调用方法: 类直接调用,
classname.foo
, 或者通过实例调用,instancename.foo
.
私有属性
就是不希望被类或实例调用的属性, 私有属性一般用1个或2个下划线开头进行命名(注意不以2个下划线结束), 比如, 对于实例属性命名方式为self.__foo
, 而对于类属性命名方式直接为__foo
;
另外1个下划线开头的私有属性, 还是可以被类或者实例直接调用的, 而2个下划线开头的就无法直接用通用方法来调用了, 而是需要把原属性名foo
替换为_classname__foo
才能调用.
不是非常需要的情况下, 建议尽量少用两个下划线开头的私有属性, 而是应该使用一个下划线开头的保护属性, 表示这个属性是保护性的, 但你要使用也依然可以直接使用.
3. 方法
方法, 就是在类中定义的函数, 表示实例的一些动态能力.
初始化函数
用def __init__(self, args...)
声明, 第一个参数必须是self
, 代表当前对象(实例)的引用, 其他参数是在对象化时(实例化)需要传入的属性值;
初始化函数在一个对象生成时(即实例化时)会被自动调用.
成员函数(实例函数)
第一个参数是self
的函数叫做成员函数(除了初始化函数), self
代表的是实例对象的引用, 如果没有self
参数, 实例将无法调用该函数;
成员函数主要是做一些和属性相关的任务, 一般是通过查询或修改类的属性等方式, 来实现实例的一部分功能;
成员函数只能被实例调用, 不能被类本身调用.
静态函数
- 使用装饰器
@staticmethod
来声明的函数叫静态函数; - 静态函数没办法引用类的属性, 包括实例属性和类属性, 这也是为什么被称为静态函数的原因;
- 静态函数一般用来做一些和属性无关的简单独立的任务, 既方便测试也能优化代码结构;
- 静态函数可以没有参数, 另外为了避免歧义, 静态函数的第一个参数不要是
self
, 因为即使是self
, 这个self
也只是代表函数的一个普通参数而已, 已经不再像成员函数的第一个self
参数那样, 代表的是实例对象的引用了; - 静态函数既可以被实例调用, 又可以被类本身调用.
类函数
- 使用装饰器
@classmethod
来声明的函数叫类函数; - 类函数的第一个参数为
cls
, 表示必须传一个类进来; - 类函数最常见的用法在于提供不同的初始化函数;
- 类函数可以显式被外界调用, 第一个参数
cls
表示类本身, 因此可以使用cls
调用类本身的初始化函数来完成初始化; - 类函数是类对象的方法, 在定义时需要在上方使用
@classmethod
进行装饰,形参为cls
, 表示类对象, 类对象和实例对象都可调用.
抽象函数
- 使用装饰器
@abstractmethod
来声明的函数叫抽象函数; - 抽象函数一般定义在抽象类中, 主要目的是要求子类必须重写该函数才能正常使用;
- 抽象函数一般通过raise异常来返回
NotImplementedError()
或者NotImplemented
.
私有函数
函数名是1个或2个下划线开头的, 但不是以2个下划线结尾的函数, 是私有函数;
函数重写
父类的某函数通过raise Exception
的方式要求子类必须重写该函数来覆盖父类原有函数.
魔法方法
魔法方法就是可以给你的类增加魔力的特殊方法, 如果你的对象实现(重载)了这些方法中的某一个, 那么这个方法就会在特殊的情况下被Python所调用, 你可以定义自己想要的行为, 而这一切都是自动发生的, 它们经常是两个下划线包围来命名的(比如__init___
, __len__
), Python的魔法方法是非常强大的所以了解其使用方法也变得尤为重要.
__init__
构造器, 当一个实例被创建的时候初始化的方法, 但是它并不是实例化调用的第一个方法.
__new__
才是实例化对象调用的第一个方法, 它只取下cls
参数, 并把其他参数传给__init___
.
___new__
很少使用, 但是也有它适合的场景, 尤其是当类继承自一个像元祖或者字符串这样不经常改变的类型的时候.
__call__
让一个类的实例像函数一样被调用.
__getitem__
定义获取容器中指定元素的行为, 相当于self[key]
.
__getattr__
定义当用户试图访问一个不存在属性的时候的行为.
__setattr__
定义当一个属性被设置的时候的行为.
__getattribute___
定义当一个属性被访问的时候的行为.
4. 实例
实例是类对象化(实例化)后的某一个具体事物;
实例可以调用的对象: 类属性、实例属性