定义类
本节目标
- 了解类与对象
- 掌握定义类的语法
- 理解Python的动态性
- 例方法
类和对象
- 两个重要概念:类(class)和对象(object,也被称为实例,instance)
- 类是某一批对象的抽象,可以把类理解成某种概念
- 对象才是一个具体存在的东西
语法
- class类名:
- 执行语句…
- 零个到多个类变量…
- 零个到多个方法…
语法说明
- 类体中的执行语句,会在定义类时自动执行
- 如果类体中没有任何代码,使用pass语句作为占位符
Python是动态语言(1)
- Python类的类变量可以动态增加或删除:程序在类体为新变量赋 值就是增加类变量
- 程序也可在任何地方为已有的类增加变量
- 也可通过del语句删除已有类的类变量
Python是动态语言(2)
- Python对象的实例变量也动态增加或删除:程序对新实例变量赋 值就是增加实例变量,
- 程序可以在任意地方为已有对象增加实例变量
- 也可通过del语句删除已有对象的实例变量
实例方法
- 函数中定义的方法,默认是实例方法
- 实例方法第一个参数会被自动绑定到方法的调用者(该类的实例)—— 因此这些实例方法至少应该定义一个参数,该参数通常会被命名为self。
创建对象和使用对象
本节目标
- 构造方法
- 创建对象
- 操作实例变量
- 操作方法
构造方法
- 造方法是一个特殊的实例方法,方法名为 _init_
- 创建对象时,自动调用构造方法
- 如果开发者没有为该类定义任何构造方法,那么Python会自动为 该类定义一个只包含一个self参数的、默认的构造方法
类的作用
- 创建对象
- 派生子类
创建对象
- 调用构造器创建对象
- 对象的作用:
- 操作对象的实例变量(包括访问实例变量的值、添加实例变量、删除实例变量)。
- 操作对象的方法(包括调用方法,添加方法,删除方法)。
操作对象的实例变量
- 问实例变量的值
- 改变:对已有的实例变量赋值
- 增加:对不存在的实例变量赋值
- 删除:用del语句
操作对象的实例方法
- 调用方法
- 改变:对已有的方法赋值
- 增加:对不存在的方法赋值
- 删除:用del语句
增加方法
- 动态增加的方法,Python不会自动将方法调用者绑定到它们的第一个参数
- 如果动态增加的方法也能自动绑定第一个参数,可借助于types模块下的MethodType进行包装
方法与实例变量总结
- Python的方法与实例变量是统一的:它们都是对象的成员。
- 取决于你对它所赋的值:
- 如果所赋的值是普通值,则是实例变量
- 如果所赋的值是函数,则是方法
实例方法与自动绑定self及使用类调用实例方法
本节目标
- 实例方法与自动绑定
- 返回self
- 类调用实例方法
实例方法与自动绑定
- 使用对象调用实例方法时,Python会自动绑定方法的第一个参数(通常建议将该参数命名为self)
- 根据第一个参数出现位置的不同,第一个参数所绑定的对象 略有区别。
- 在构造方法中引用该构造方法正在初始化的对象。
- 在普通实例方法中引用调用该方法的对象。
self不可省略
- Python的一个方法调用另一个方法时,不可以省略self。
- Python的类、对象有点类似于一个命名空间,因此在调用类、对 象的方法时,一定要加上“类.”或“对象.”的形式。
方法返回self
- self参数可当成实例方法的返回值
- 把self参数作为返回值,则何多次连续调用方法(只要该方法也返回self)
类调用实例方法
- 使用类调用实例方法时,Python不会绑定方法的第一个参数
- 如果实例方法定义了一个self参数,使用类调用实例方法时,必须 手动传入参数值
类方法与静态方法
本节目标
- 类方法与静态方法
- 类方法的参数与自动绑定
- 静态方法
类方法与静态方法
- 类方法和静态方法有点相似, 它们都推荐使用类来调用(其实也可使用对象来调用)。
类方法
- 定义类方法
- 使用@classmethod修饰
- 方法的第一个参数定义为cls,用类调用该方法时该参数会自动绑定
方法的自动绑定
- 用类调用类方法,第一个cls参数会自动绑定
- 用对象调用类方法,其实就相当于用类调用类方法,因此第一个参 数同样会自动绑定到该对象的类
静态方法
- 定义静态方法
- 使用@staticmethod修饰
- 对方法参数没有要求,无论如何都不会自动绑定
案例实操:函数装饰器应用
本节目标
- 函数装饰器广泛应用
- 函数装饰器的定义和使用
- 函数装饰器的本质
函数装饰器的广泛应用
- @staticmethod和@classmethod的本质就是函数装饰器,
- staticmethod和classmethod都是Python内置的函数 。
函数装饰器的用法
- 使用@符号引用已有的函数(比如@staticmethod、@classmethod)后,可用于修饰其他函数
函数装饰器的本质
- 当程序使用〃@函数〃(比如函数A)装饰另一个函数(比如函数B)时,实际上完成如下两步:
- (1)将被修饰的函数(函数B)作为参数传给@符号引用的函数(函数 A)。
- (2)将函数B替换(装饰)成第1步的返回值。
函数装饰器与AOP
- 通过使用函数装饰器,可在被被修饰函数之前、之后、抛出异常后增加某种处理逻辑的方式,就是其他编程语言中的AOP (Aspect Orient Programming,面向切面编程)。
类变量和实例变量
本节目标
- 类变量与实例变量的区分
- 类调用类变量
- 对象调用实例变量、类变量
- 合成属性(实例变量)
类变量与实例变量
- 类变量:在类空间或通过类引用赋值的变量
- 实例变量:通过对象引用或self引用赋值的变量
类、对象可访问类变量
- 通过类,可获取、修改类变量的值
- 通过对象,可获取类变量的值
- 如果尝试通过对象对“类变量”赋值,就变成了新增实例变量
类不能访问实例变量
- 实例变量不在类空间下,类不能访问实例变量
使用property合成属性
- 使用property合成属性(相当于实例变量):
- property(fget=None,fset=None,fdel=None,doc=None)
- 使用property()函数定义属性时也可根据需要只传入少量参数。如合成只读属性就无需设置fget参数
使用@property装饰器
- 还可使用@property装饰器来修饰getter方法,使之成为属性。
- 如果需要为属性配备setter方法,则用@属性名。setter装饰器修饰setter方法
隐藏与封装
本节目标
- 理解封装
- Python的隐藏方式
- 合理封闭的方式
- Python隐藏的本质
封装
- 封装:
- 合理隐臟
- 合理暴露
Python的隐藏机制
- 规定:只要将命名为以双下画线开头的,Python就会把它们隐藏起来。
封装
- 将实例变量、工具方法命名为以双下画线开头,这就实现了隐藏
- 提供方法(默认暴露)来操作实例变量
双下画线的本质
- 双下画线只是Python的一个小技巧:Python会"偷偷"地改变以双 下画线开头的成员名,会在这些成员名前添加单下画线和类名
- 程序可通过这种方式来调用“被隐藏”的成员————一般不建议这么干
隐藏机制的总结
- Python并未提供真正的隐藏机制,Python类中所有成员默认都是公开的;
- 若需要隐藏某些成员,将它们命名为以双下画线开头即可。其实也 依然可通过添加“下画线加类名”前缀来调用被隐藏的成员。
继承与多继承及重写父类方法
本节目标
- 继承语法
- 继承机制 - 复用父类方法
- 多继承
- 重写父类方法
继承语法
- 将多个父类放在子类之后的圆括号里。语法格式如下:
- class SubClass(SuperClass1,SuperClass2,…):
- #类定义部分
• 从子类的角度来看,子类扩展(extend)了父类;但从父类的角度来 看,父类派生(derive)出子类。
继承=复用
- 子类继承父类,即可获得父类的方法,这样子类即可复用父类的方法
多继承
- Python支持多继承。
- 但如果不是很有必要,则尽量不要使用多继承,而是使用单继承
多个父类中方法重名
- 如果多个父类中包含了同名的方法,排在前面的父类中的方法会"遮蔽"排在后面的父类中的同名方法。
重写父类方法
- 子类重新定义与父类 法同名的方法,称为:重写父类方法
调用被重写方法和调用父类构造方法
本节目标
- 调用父类被重写的方法參
- 使用未绑定方法调用父类构造方法
- 使用super()函数调用父类构造方
调用被重写的方法
- 使用未绑定方法:以父类类名调用被重写方法,将self作为第一个参 数传入
class Employee:
def work (self):
print('执行996的工作时间,工作很努力')
class Manager (Employee):
def work (self):
print('经理工作更加拼命')
def relax (self):
print('经理正在休闲放松中')
# 默认情况下,直接调用work方法,总是调用子类重写之后的方法
# self.work()
# 如果此处希望调用父类的方法,可通过未绑定方法来调用
# 类名调用方法:未绑定方法,因此需要显式传入第一个参数
Employee.work(self)
mg = Manager()
mg.relax()
为何要调用父类构造方法
当子类提供自己的构造方法时,子类构造方法需要调用父类的构造方法
class Employee:
def __init__ (self, salary):
self.salary = salary * 1.8
class Manager (Employee):
def __init__ (self, salary, title):
# 当子类的初始化操作与父类初始化操作相同时
# 程序不应该直接复制父类初始化代码---这样不利于后期的项目升级
# 因此,子类构造器应该直接调用父类的构造方法
self.salary = salary * 2.1
self.title = title
mg = Manager(6800, '项目经理')
print(mg.salary)
print(mg.title)
调用父类构造方法(1)
- 使用未绑定方法:既然构造方法也是实例方法,当然可以通过这种方式来调用。
class Employee:
def __init__ (self, salary):
self.salary = salary * 1.8
class Manager (Employee):
def __init__ (self, salary, title):
# 当子类的初始化操作与父类初始化操作相同时
# 程序不应该直接复制父类初始化代码---这样不利于后期的项目升级
# 因此,子类构造器应该直接调用父类的构造方法
# self.salary = salary * 2.1
# 方法一:使用未绑定方法调用父类构造方法:需要显式传入self参数
Employee.__init__(self, salary)
self.title = title
mg = Manager(6800, '项目经理')
print(mg.salary)
print(mg.title)
调用父类构造方法(2)
- 使用super()函数调用父类的构造方法。
class Employee:
def __init__ (self, salary):
self.salary = salary * 2.3
class Manager (Employee):
def __init__ (self, salary, title):
# 当子类的初始化操作与父类初始化操作相同时
# 程序不应该直接复制父类初始化代码---这样不利于后期的项目升级
# 因此,子类构造器应该直接调用父类的构造方法
# self.salary = salary * 2.1
# 方法二:用super()函数来调用父类构造器方法:无需要传入self参数
super().__init__(salary)
self.title = title
mg = Manager(6800, '项目经理')
print(mg.salary)
print(mg.title)