python 类的基础概念(二)
1、封装 enclosure
【1】封装是指隐藏类的实现细节,让使用者不关心这些细节
【2】封装的目的是让使用者通过尽可能少的方法(或属性)操作对象
2、私有属性和方法
【1】python类中以双下划线('__') 开头,不以双下划线结尾的标识符为私有成员,私有成员或只能用类内的方法进行访问和修改
【2】以__开头的实例变量为私有属性
【3】以__开头的方法为私有方法
In [175]: # 此示例示意私有属性和私有方法
...: class A:
...: def __init__(self):
...: self.__p1 = 100 # 私有属性
...:
...: def show_A(self):
...: print('self.__p1: ', self.__p1)
...: self.__m1() # 调用自己的方法
...:
...: def __m1(self): # 私有方法
...: print("__m1(self)方法被调用")
...:
...:
...: a = A()
...: a.show_A() # a.__p1: 100
...: # print(a.__p1) # 出错,在类外部不能访问a的私有属性__p1
...: # a.__m1() # 出错,不能调用私有方法
...:
...:
...: class B(A):
...: pass
...:
...:
...: b = B()
...: # print(b.__p1) # 出错, 子类对象不能访问父类中的私有成员
...: # b.__m1() # 出错
...:
self.__p1: 100
__m1(self)方法被调用
3、多态 polymorphic
什么是多态:
字面意思: 多种状态
多态是指在有继承/派生关系的类中,调用基类对象的方法,实际能调用子类的覆盖方法的现象叫多态
状态:
静态(编译时状态)
动态(运行时状态)
【注】: 多态调用方法与对象相关,不与类相关
Python的全部对象都只有"运行时状态(动态)", 没有"C++语言"里的"编译时状态(静态)"
In [176]: # 此示例示意python中的运行时状态 ...: class Shape: ...: def draw(self): ...: print('Shape的draw方法被调用') ...: ...: ...: class Point(Shape): ...: def draw(self): ...: print("正在画一个点") ...: ...: ...: class Circle(Shape): ...: def draw(self): ...: print("正在画一个圆") ...: ...: ...: def my_draw(s): ...: s.draw() # 此处调用哪儿方法呢? 此处显示出'动态' ...: ...: ...: s1 = Circle() ...: s2 = Point() ...: my_draw(s2) ...: my_draw(s1) 正在画一个点 正在画一个圆
4、多继承 multiple inheritance
多继承是指一个子类继承自两个或两个以上的基类。
class 类名(基类名1, 基类名2, ...):
pass
【1】 一个子类同时继承自多个父类,父类中的方法可以同时被继承下来
In [177]: # 此示例示意用多继承来派生新类 ...: class Car: ...: def run(self, speed): ...: print('汽车以', speed, 'km/h的速度行驶') ...: ...: ...: class Plane: ...: def fly(self, height): ...: print("飞机以海拔", height, '米的高度飞行') ...: ...: ...: class PlaneCar(Car, Plane): ...: '''PlaneCar类同时继承自汽车和飞机''' ...: ...: ...: p1 = PlaneCar() ...: p1.fly(10000) ...: p1.run(300) 飞机以海拔 10000 米的高度飞行 汽车以 300 km/h的速度行驶
【2】 如果两个父类中有同名的方法,而在子类中又没有覆盖此方法时,调用结果难以确定
5、多继承存在的问题
【1】标识符(名字空间)冲突的问题
In [178]: # 此示例示意多继承名字冲突问题 ...: # 小张写了一个类A ...: class A: ...: def m(self): ...: print('A.m()被调用') ...: ...: ...: # 小李写了一个类B ...: class B: ...: def m(self): ...: print('B.m()被调用') ...: ...: ...: # 小王感觉小张和小李写的两个类自己可以用 ...: class AB(A, B): ...: pass ...: ...: ...: ab = AB() ...: ab.m() # 请问会发生什么? ...: ...: a=AB.__mro__ ...: print(a) A.m()被调用 (<class '__main__.AB'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
【2】 如果两个父类中有同名的方法,而在子类中又没有覆盖此方法时,调用结果难以确定
要谨慎使用继承
6、多继承的 MRO (Method Resolution Order) 问题
类的 __mro__ 属性
此属性用来记录类的方法查找顺序
In [142]: class A: ...: def go(self): ...: print('A') ...: # super().go() # error ...: ...: class B(A): ...: def go(self): ...: print('B') ...: super().go() ...: ...: class C(A): ...: def go(self): ...: print('C') ...: super().go() ...: ...: class D(B, C): ...: def go(self): ...: print('D') ...: super().go() ...: ...: d = D() ...: d.go() # ...: D B C A In [143]: print(D.__mro__) (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
7、环境管理器
类内有__enter__ 和 __exit__实例方法的类被称为环境管理器
能够用with进行管理的对象必须是环境管理器
说明:
__enter__将在进入with语句时被调用并返回由as变量绑定的对象
__exit__将在离开with语句时被调用,且可以用参数来判断在离开with语句时是否有异常发生并做出相应的处理
# 此示例示意 让一个自定义的类创建的对象能够使用with语句 class A: '''此类的对象可用于with语句进行管''' def __enter__(self): print("已经进入with语句,资源分配成功!") return self # <<<--此处返回的对象将由 as 变量绑定 def __exit__(self, exc_type, exc_val, exc_tb): print("已经离开with语句,资源释放成功!") if exc_type is None: print("当离开with语句时没有发生异常") else: print('有异常发生,异常类型是:', exc_type, '异常值是:', exc_val) with A() as a: print("这是with中的语句") raise ValueError("故意制造的异常")