面向对象(补充)上一部分
- 描述器
- 生命周期
- 内存管理
- 面向对象三大特性
- 类的设计原则
描述器
- 描述器是一个对象,用来描述其他对象属性的操作;作用是对属性的操作做验证和过滤。
- 前面只读属性案例中就是用到了描述器。
- 在对象的内部增加一个描述器,可以接管对象属性的增删改查操作。
class Age:
def __get__(self, instance, owner): # instance是拥有 age属性的对象
pass
def __set__(self, instance, value):
instance.v = value # 将变量的值绑定在 Person的实例中
def __delete__(self, instance):
pass
class Person:
age = Age()
# age实例是 p1和 p2两个对象所共享的,所以 Age对象及实例不应该具有属性,只单纯地提供方法即可
p1 = Person()
p1.age = 19 # 调用 __set__()
print(p1.age) # 调用 __get__()
p2 = Person()
p2.age = 20
print(p2.age) # 20
-
资料描述器和非资料描述器
- 资料描述器:实现了
__get__()
和__set__()
- 非资料描述器:仅仅实现了
__get__()
- 资料描述器:实现了
- 实例属性和描述器重名时,操作的优先级如下:
- 资料描述器 > 实例属性 > 非资料描述器
生命周期
- 表示一个对象从创建到释放的过程
class Person:
__count = 0
def __init__(self):
Person.__count += 1
def __del__(self):
Person.__count -= 1
@classmethod
def log(cls):
print("we have %d people" % cls.__count)
p1 = Person()
Person.log() # we have 1 people
p2 = Person()
Person.log() # we have 2 people
内存管理机制
- 万物皆对象,不存在基本数据类型
- 对于相等的整数 [-5, 正无穷) 和短小的字符串,Python会进行缓存,不会创建多个对象
n1 = 1
n2 = 1
print(id(n1), id(n2)) # 1708655056 1708655056
- 容器对象:存储的对象,仅仅是其他对象的引用(列表)
- 内存回收
-
引用计数
- 一个对象会记录着自身被引用的个数
- 每增加一个引用,引用数+1,减少一个引用,引用数-1
- 引用数为0的时候,会被当做垃圾进行回收
- 会出现连个对象循环引用的问题
-
垃圾回收
- 从经历过引用计数机制但仍然未被释放的对象中,进行内存释放
- 新增的对象个数 - 消亡对象的个数达到一定阈值时才进行垃圾检测
-
分代回收
- 分代回收是垃圾回收的高效解决方案,不需频繁地进行垃圾检测
- 存活时间越久的对象,越不可能在后面的过程中变成垃圾
- 设立0, 1, 2三代对象集合,对其中的对象进行不同频率的检测
- 第一次检测存活下来的,从0代纳入1代,0代检测一定次数后开始检测1代,以此类推
-
引用计数
- 深拷贝和浅拷贝
- 浅拷贝
a = [1, 2, 3] b = a print(id(a), id(b)) # 2229855665608 2229855665608
- 深拷贝
import copy a = [1, 2, 3] c = copy.deepcopy(a) print(id(a), id(c)) # 2229855665608 2229861709896
- copy和deepcopy的区别
imageimport copy a = [1, 2, 3] b = [4, 5, 6] c = [a, b] d = copy.deepcopy(c) e = copy.copy(c)
- 使用
copy
拷贝可变类型时,进行单层次的深拷贝,若拷贝的是不可变类型,则进行浅拷贝。
面向对象三大特性
- 封装、继承、多态
继承
- 非私有的属性和方法可以被继承,继承不是拷贝了资源,而是具有了资源的访问权,资源的存储位置在父类中,实现资源重用。
-
Python
中可以使用多继承。
# 所有的类都继承了 object类
# 所有的类对象都由 type实例化出来
class A:
pass
class B:
pass
class C(A, B): # C 类继承了 A和 B类
pass
print(C.__bases__) # (<class '__main__.A'>, <class '__main__.B>)
print(int.__base__) # (<class 'object'>)
print(bool.__base__) # (<class 'int'>)
-
几种继承的形式
image -
资源查找顺序
- 单继承链:C-->B-->A
- 无重叠多继承链:按照单继承链深度优先查找(C-->B-->A-->D-->E)
- 有重叠多继承链:广度优先查找(C-->B-->D-->A)
-
资源覆盖
- 在优先级较高的类中重新定义了同名的属性和方法,再次调用时,会调用到优先级较高类中的资源,并不是相关的资源在内存上被覆盖了,而是调用优先级出现了变化
-
self
和cls
# 谁调用方法,self 和 cls 就是谁
# 带着参数去找方法
class A:
def show(self):
print(self)
@classmethod
def tell(cls):
print(cls)
class B(A):
pass
B.tell() # <class '__main__.B'>
B().show() # <__main__.B object at 0x027674D0>
- 资源的累加
class A:
def __init__(self):
self.x = 2
class B(A):
pass
class C(A):
def __init__(self):
self.y = 1
class D(A):
def __init__(self):
self.y = 1
class E(A):
def __init__(self):
super().__init__() # 会调用 A的构造函数,参数可以省略
# A.__init__(self) //和上面等价,要传参
self.y = 1
b = B()
print(b.x) # 2, 调用了父类的构造函数,b 调用,x就是 b的
c = C()
print(c.y) # 1, C有了构造函数,就调用 C的,A的构造函数不会被调用
print(c.x) # 报错,没有这个属性
e = E()
print(e.x, e.y) # 2, 1
多态
- Python是动态类型的语言,不需要严格意义上的多态
def test(obj):
obj.func()
# 只要传入的参数有 func()这个方法,就可以传入进行执行,不用进行类型检测。
# 不需要按照其他静态语言那样沿着继承链进行方法调用形成多态
类的设计原则
- 单一职责原则:一个雷只负责一项职责
- 开放封闭原则:对外扩展开放,对内修改关闭
- 里式替换原则:子类所继承下来的属性和方法都需能够合理地使用
- 接口分离原则:功能一致的方法应该重新组成新的接口/类,进行细分
- 依赖倒置原则:高层模块不应该直接依赖低层模块,核心是面向接口编程
网友评论