对象概念
- 万物皆对象
- 对象是具体物体:拥有属性 拥有行为 把很多零散的东西,封装成为一个整体
- python是一门特别彻底的面向对象的语言
- OOP在解决问题的时候关注解决问题的所需要的对象(划分对象)
- 面向对象在解决问题的时候关注解决问题的过程(步骤),把一个任务,分解成具体的一个步骤
- 面向过程——>面向对象:列出任务具体现实步骤、试图分离这些步骤的功能代码块、将这些功能代码块划分到某个对象中、根据这个对象以及对应的行为抽象出对应的类
定义类
在解决问题的的时候关注的是解决问题所需要的对象
设计类:根据对象以及对应行为抽象出对应的类
# 经典类
class Money:
nam = "yl"
age = 22
print(Money.__name__) # Money 类名
Money == 66 # Money是当前类的类名,指向类空间的类名;也是变量名
print(Money)
# 实例化一个对象
one = Money()
print(one.__class__) # 对象指向的类
print(one)
创建对象时底层运作
属性相关
属性 VS 变量 是否存在宿主(对象属性 类属性)
变量是可以改变的量值
属性是属于某个对象的特性,属性只能通过对象来访问,所以必须先找到对象,对象也是通过变量名来引用,既然是变量也有对应的访问权限
对象属性
# 对象属性相关
class Person:
pass
# 根据类,创建一个对象
p = Person()
# 增加属性
p.age = 18
print(p.age) # 18
# 修改属性
p.age = 22
print(p.age) # 22
# 删除属性
del p.age
p.age = 18
print(p.age)
print(p.__dict__) # 当前对象所有的属性
增加属性
# 类也是一个对象
# 类属性-增加属性方式一
# 类名.类属性 = 值
Money.count = 1
print(Money.count)
# {'count': 1, '__weakref__': <attribute '__weakref__' of 'Money' objects>, 'nam': 'yl', '__doc__': None, '__module__': '__main__', 'age': 22, '__dict__': <attribute '__dict__' of 'Money' objects>}
print(Money.__dict__)
# 类属性-增加属性方式二-直接在类里面增加
print(Money.age)
# 通过类对象访问类的属性
# 一个对象的__class__可以修改,现在对象自身查找属性,没有再去类里面
print(one.nam)
# 修改类的属性
# 不可以通过对象修改类的属性
# 对象只会增加自己的属性或者修改自己的属性
Money.count = 432
print(Money.count)
# 删除类的属性 不能通过对象来删除 只能删除直系属性
del Money.age
# 类属性的内存存储问题:一般情况存储在__dict__的字典中,有些内置对象没有__dict__属性
# 一般对象可以直接修改__dict__属性
# 类本身的__dict__只是可读默认无法修改 通过setattr方法修改
class Money:
age = 19
height = 180
print(Money.__name__)
# 实例化一个对象
one = Money()
one.age = 19
one.height = 180
print(one.__dict__) # 对象属性
one.__dict__ = {"name":"yl","age":18}
one.__dict__["name"] = "dasf"
print(one.name) # yl
# Money.__dict__={"sex":"男"} error
# 类属性被各个对象所共享
two = Money()
print(two.nam) # one、two对象共享Money类的属性
** 限制对象属性的添加**
# 限制对象属性的添加
class Person:
__slots__ = ["age"] # 列表里面是对象可以添加的属性
def eat(self): # 补全第一个参数,实例方法
print("实例方法", self)
@classmethod
def leiff(cls): # 补全第一个参数,类方法
print("类方法", cls)
@staticmethod
def jtff(): # 补全第一个参数,静态方法
print("静态方法")
p1 = Person()
p1.age = 12 # 对象只能添加age
print(p1.age)
方法相关
- VS 函数 调用方式不同
- 方法划分
class Person:
def eat(self): # 补全第一个参数,实例方法
print("实例方法", self)
@classmethod
def leiff(cls): # 补全第一个参数,类方法
print("类方法", cls)
@staticmethod
def jtff(): # 静态方法
print("静态方法")
p1 = Person()
# 方法划分
# 划分依据:方法的第一个参数必须接收的数据类型
# 实例方法:默认第一个参数需要接收到一个实例
# 类方法:默认第一个参数需要接收到一个类
# 实例方法:第一个参数啥也不默认接收
# 不管是哪一种方法都是存储在类当中、不同类型的方法调用的方式不同
# 实例方法 (对象是实例):默认第一个参数需要接收到一个实例
p1.eat() # 实例方法,p1是实例
# 类方法:默认第一个参数接收一个类
Person.leiff() # 类方法,Person是类
# 静态方法:第一个参数啥也不接收
Person.jtff() # 静态方法
- 方法存储
# 方法的存储,方法和属性都存储在类的dict字典里面
# 但是方法都是存储在类当中,不在实例中,属性可以在实例中
# print(p1.__dict__)
print(Person.__dict__)
- 实例方法
# 实例方法
class Person:
def eat(self, food): # 补全第一个参数,实例方法
print("实例方法", food)
# 标准调用
p = Person()
p.eat("土豆") # 只需要填写food参数,self参数会自动把实例传递过来
# 类调用
print(Person.eat)
Person.eat(123,"土豆")
# 间接调用
func = Person.eat
func(123,"土豆")
- 类方法
# 类方法
class Person:
@classmethod
def leiff(cls, a): # 补全第一个参数,类方法
print("类方法", cls, a)
# 类调用
Person.leiff(1234)
# 实例调用,实例会被忽略
p = Person()
p.leiff(909)
func = Person.leiff
func(123)
- 静态方法
# 静态方法
class Person:
@staticmethod # 将一个方法转换成静态方法
def jtff(n): # 补全第一个参数,静态方法
print("静态方法", n)
Person.jtff("ghhg")
p = Person()
p.jtff("nmhbhk")
- 不同类型方法访问不同类型属性
# 不同类型方法访问不同类型属性
class Person:
age = 22 # 类属性
p = Person()
p.num = 10 # 实例属性
# 类属性
print(Person.age)
p.age # 先在自己dict里面有没有,没有会沿着__class__找到对应一个类对象,去类对象里面找
# 实例属性
print(p.num) # 不可能通过类访问实例属性
类相关
- 元类
# 类相关补充,元类:创建类对象的类
num = 10
print(num.__class__) # 类是int是个类型
print(int.__class__) # 所有int、布尔、str等类型都是type元类创建,type是最上面
# type可以创建类
def run(self):
print(self)
dog = type("Dog", (), {"count": 0, "run": run}) # 类名称dog 父类() 属性方法dict{}
print(dog.__dict__)
d = dog()
print(d.count)
# 类的创建流程
# 1、先是检测类对象中是否有没有明确元类__metaclass__属性
# 2、检测父类里面有没有明确__metaclass__属性
# 3、检测模块中是否存在__metaclass__ = xxx 模块里面找
# 4、通过内置的type元类创建这个类的对象
# __metaclass__指明元类
# class Person:
# __metaclass__ = xx # 可以指明元类,可以是type也可以是自己写的元类
# pass
# # 方式二
# class Person(metaclass= xx):
# pass
- 类的描述
# 类的描述:方便生成项目文档
class Person:
"""
关于这个类的描述,类的作用,类的构造函数等等;
Attibutes类属性的描述:
"""
@staticmethod # 将一个方法转换成静态方法
def jtff(n, distance, step): # 补全第一个参数,静态方法
"""
这个方法的作用
:param n: 补充参数含义、类型、是否有默认值
:param distance:
:param step:
:return: 返回值结果含义、返回的数据类型
"""
print("静态方法")
return distance
- 注释文档的生成
# 注释文档的生成
# 方式一
# 使用内置模块 pydoc
# 查看文档描述:python3 -m pydoc 模块名称(python3 -m pydoc 面向对象)只能在命令框查看
# 启动本地服务,浏览文档:python3 -m pydoc -p 6666(要使用英文名称,以网页的方式发送,内建模块也发送了)
# 启动本地服务,浏览文档:python3 -m pydoc -b 自己给端口号推荐使用
# 生成指定模块的html文档:python3 -m pydoc -w 模块名称(python3 -m pydoc -w oop)生成HTML文件
# 方式二
# 使用第三方模块:sphinx、epydoc、doxygen
私有化属性
# 私有化属性
# python没有真正的私有化支持,但是可以使用下划线完成伪私有的效果
# 1、 x:公有属性:类内部访问、子类内部访问、模块类其他位置访问、跨模块访问都可以访问
# 2、_y:受保护属性:类内部访问、子类内部访问可以访问;
# 模块类其他位置访问可以强行访问但是有警告;
# 跨模块访问:import形式导入可以强行访问但是有警告
# from module import * 形式导入有__all__指明变量可以强行访问但是有警告 __all__ = ["_a", "_y",....]
# 没有__all__指明变量不可以强行访问
# 3、__z:私有属性:类内部访问可以访问;
# 子类内部访问、模块类其他位置不可访问
# 跨模块访问参照单下划线开头变量的访问原则
名字重整机制
# 私有属性的实现机制:名字重整机制 重改__x为另外一个名称如_类名__x 名字被改了
# 目的:防止外界直接访问、防止被子类同名属性覆盖
class Animal:
__x = 10
def test(self):
print(Animal.__x)
print(self.__x)
pass
print(Animal.__dict__) # {'_Animal__x': 10, '__module__': '__
print(Animal._Animal__x) # 通过其他手段访问私有属性 python是伪私有
# 私有化属性应用场景:数据保护、数据过滤
class Person:
def __init__(self): # 实例方法,当我们创建好实例对象会自动调用方法初始化对象
self.__age = 18
def setAge(self, value):
if isinstance(value, int) and 0 < value < 200: # 判定是不是数值类型
self.__age = value
else:
print("你输入的数据有问题,请重新输入")
def getAge(self):
return self.__age
# 自带age属性
p1 = Person()
p1.__age = 67 # 不是私有属性,只是增加的新的属性
p1.setAge(23)
print(p1.getAge())
p2 = Person()
# print(p2.age)
print(p1.__dict__) # 有两个age{'_Person__age': 18, '__age': 67},一个是改名的私有属性,一个不是
变量添加下划线的规范
xx_:与关键字区别 class_
__xx__: 系统内置的方法 避免这个命名方式 __dict__
只读属性
一个属性(一般指实例属性,只能读取,不能写入)
# 只读属性,一般特质实例属性,只能读取不能写入
# 有些属性只限于在内部根据不同场景进行修改,对外界来说不能修改只能读取,比如网速属性 网络状态属性(外界只能看到不能修改,内部字典修改)
# 方式一
# 全部隐藏:私有化既不可以读也不可以写 “属性前置双下划线”
# 部分公开:通过公开方法 公开读操作
class Person:
def __init__(self): # 实例方法,当我们创建好实例对象会自动调用方法初始化对象
self.__age = 18
# 部分公开:公开方法读取
def getAge(self):
return self.__age
# 优化方案 往后使用这种方式
class Person(object):
def __init__(self): # 实例方法,当我们创建好实例对象会自动调用方法初始化对象
self.__age = 18
# 部分公开:公开方法读取
@property # 装饰器可以以使用属性的方式使用方法age()
def age(self):
return self.__age
p1 = Person()
print(p1.age) # 不是p1.age()
# p1.age = 99 # 会报错而不是增加新的属性,只能读取不可以设置
# property可以将一个属性的操作方法(删改查)关联到某一个属性中
经典类和新式类概念加粗样式
# 经典类:没有继承object
# 新式类:继承object python3.x默认继承
print(Person.__bases__) # 父类 (<class 'object'>,)
# propety在新式类和经典类中使用方式
# 新式类
# 方式一
class Person:
def __init__(self): # 实例方法,当我们创建好实例对象会自动调用方法初始化对象
self.__age = 18
def setAge(self, value):
if isinstance(value, int) and 0 < value < 200: # 判定是不是数值类型
self.__age = value
else:
print("你输入的数据有问题,请重新输入")
def getAge(self):
return self.__age
age = property(getAge, setAge) # age和setAge和getAge方法关联起来了 age是新的属性
p = Person()
print(p.age)
p.age = 90
print(p.age)
# 方式二
class Person:
def __init__(self): # 实例方法,当我们创建好实例对象会自动调用方法初始化对象
self.__age = 18
@property # 单独使用关联读取
def age(self):
return self.__age
@age.setter # 关联设置 @age.deleter 关联删除
def age(self, value):
self.__age = value
p = Person()
print(p.age)
p.age = 90
print(p.age)
只读属性方式二
# 只读属性方式二
class Person:
# 当我们通过实例.属性 = 值,给实例增加一个属性或者说修改一下属性值的时候都会调用这个方法
# 在这个方法内部,才会真正把这个属性以及对应的数据存储到__dict__字典里面
def __setattr__(self, key, value):
print(key, value)
# 1、判定key是否是设置的只读属性的名称
if key == "age" and key in self.__dict__.keys():
print("只读属性,不能设置数据")
# 2、不是只读属性的名称,真正给它添加到实例里面去
else:
self.__dict__[key] = value
p = Person()
p.age = 18 # 第一次新增操作,可以加入字典
p.age = 12 # 第二次修改操作,不可以
p.name = "yl"
print(p.__dict__)
print(p.age)
内置特殊属性
# 内置特殊属性
class Person:
"""
这是一个人类
"""
age = 19
def __init__(self): # 实例方法,当我们创建好实例对象会自动调用方法初始化对象
self.name = "yl"
def run(self):
print("run")
# 类属性
print(Person.__dict__)
print(Person.__bases__) # 类的所有父类构成的元组
print(Person.__doc__) # 类的文档字符串
print(Person.__name__) # 类名
print(Person.__module__) # 类定义所在的模块
# 实例属性
# __dict__
# __class__实例对应的类
p = Person()
print(p.__class__) # <class '__main__.Person'>
私有化方法
# 私有化方法
class Person:
def __run(self):
print("私有化方法")
def _Person__run(self): # 不能注意命名
print("覆盖上面的__run")
p = Person()
p._Person__run()
print(Person.__dict__) # 改名'_Person__run'
内置特殊方法
1、信息格式化操作
# 内置方法
# 实现内置方法会完成特定的功能 实例化一个对象 如果没有重新实现内置方法会采用系统默认的
# 信息格式化操作:__str__、__retr__
class Person:
def __init__(self, n, a):
self.name = n
self.age = a
def __str__(self):
return "这个人的姓名:%s,这个人的年龄:%s"% (self.name, self.age)
# def __repr__(self):
# return "dssfdf"
p1 = Person("yl",16)
print(p1.name)
print(p1.age)
print(p1) # 方法一 检测有没有对应的相关的__str__ 打印return值
s = str(p1) # 方法二 同上打印__str__返回值 s是str
print(s)
print(repr(p1)) # 实例本质信息,类型、内存地址等 面向开发人员 没有实现则默认 实现了则采用实现的函数
import datetime
t = datetime.datetime.now()
print(t) # str方法
temp = repr(t)
print(temp) # 面向开发人员 repr方法
result = eval(temp) # 面向解释器 又变回来了
print(result)
2、调用操作
使得对象具备当作函数来调用的能力
# 调用操作:__call__使得对象具备当作函数来调用的能力
class Person:
def __call__(self, *args, **kwargs):
print("xxx", args, kwargs)
pass
p1 = Person()
p1(1, 23, 53443, name="yangliu") # p1对象——>函数传参 xxx (1,23,53443)
def createPen(p_type, p_color):
print("创建了一个%s这个类型的画笔,他是%s颜色" % (p_type, p_color))
createPen("钢笔", "红色")
createPen("铅笔", "黑色")
createPen("铅笔", "灰色")
import functools
def createPen(p_color, p_type):
print("创建了一个%s这个类型的画笔,他是%s颜色" % (p_type, p_color))
pen = functools.partial(createPen, p_type="钢笔") # 偏函数,参数要一一对应
pen("红色") # "红色"给第一个参数createPen(p_color, p_type) p_color
class PenFunctory:
def __init__(self, p_type):
self.p_type = p_type
def __call__(self, p_color):
print("创建了一个%s这个类型的画笔,他是%s颜色" % (self.p_type, p_color))
gangbif = PenFunctory("钢笔")
gangbif("红色")
gangbif("黑色")
gangbif("灰色")
gangbif = PenFunctory("铅笔")
gangbif("红色")
gangbif("黑色")
gangbif("灰色")
3、索引操作
可以对一个实例对象进行索引操作
# 实例对象当作字典进行索引操作
class Person:
def __init__(self):
self.cache = {}
def __setitem__(self, key, value):
print("setitem:", key, value)
self.cache[key] = value # 传递到方法里面
def __getitem__(self, item):
print("getitem:", item)
return self.cache[item]
def __delitem__(self, key):
print("delitem:", key)
del self.cache[key]
p = Person()
p["name"] = "yiu" # setitem:name yiu
print(p["name"]) # getitem:name yiu
del p["name"] # delitem: name
4、切片操作
# 切片操作
class Person:
def __init__(self):
self.items = [1, 3, 4, 2, 42, 234]
def __setitem__(self, key, value):
# print("setitem:", key, value)
self.items[key] = value
def __getitem__(self, item):
print("getitem:", item)
def __delitem__(self, key):
print("delitem:", key)
p = Person()
p[0: 4: 2] = ["a", "b"] # 修改
print(p.items) # ['a', 3, 'b', 2, 42, 234]
5、比较操作
# 比较操作
@functools.total_ordering # 自动补全dict里面方法
class A:
def __init__(self, age, height):
self.age = age
self.height = height
# ==
def __eq__(self, other): # 另外一个
print(other)
return self.age == other.age
# !=
def __ne__(self, other):
print("xxx")
return self.age == other.age
# >
def __gt__(self, other):
pass
# >=
def __ge__(self, other):
pass
# <
def __lt__(self, other):
pass
# <=
def __le__(self, other):
pass
pass
a1 = A(18, 179)
a2 = A(18, 190)
print(a1 == a2)
print(a1 != a2)
print(A.__dict__)
# 上下文布尔值
class A:
def __init__(self):
self.age = 3
self.height = 159
def __bool__(self): # 只有age>=18 才为True
return self.age >= 18
pass
p = A()
if p: # age为不为True
print("xxssx")
6、遍历操作
# 遍历操作
# 方式一 for in __getitem__
class Person:
def __init__(self):
self.result = 1
def __getitem__(self, item):
self.result += 1
if self.result >= 6:
raise StopIteration("停止遍历") # 抛出异常
return self.result
pass
p = Person()
for i in p:
print(i) # 2 3 4 5
# 方式二 迭代器要实现__iter__ __next__
class Person:
def __init__(self):
self.result = 1
# def __getitem__(self, item):
# self.result += 1
# if self.result >= 6:
# raise StopIteration("停止遍历") # 抛出异常
# return self.result
def __iter__(self): # 优先级高于__getitem__
self.result = 1 # 迭代器复用
print("iter")
# return iter([1, 2, 3, 4, 3, 32])
return self # 把当前实例返回当初迭代器(前提有next)
def __next__(self):
self.result += 1
if self.result >= 6:
raise StopIteration("停止遍历") # 抛出异常
return self.result
pass
p = Person()
# for i in p:
# print(i)
print(next(p)) # 2
print(next(p)) # 3
# 迭代器
class Person:
def __init__(self):
self.age = 1
def __iter__(self):
print("iter")
self.age = 1
return self
def __next__(self):
self.age += 1
if self.age >= 6:
raise StopIteration("停止遍历") # 抛出异常
return self.age
# next()
p = Person() # 本身就是迭代器
# pt = iter(p)
# print(pt is p) # True
for i in p: # 一个可迭代对象一定可以通过for in 访问,可以通过for in 遍历访问不一定是可迭代对象
print(i)
# iter函数的使用:将对象转换成对应的迭代器使用
l = [12, 3, 3, 23, 45]
it = iter(l)
print(it) # list_iterator 列表迭代器
# 使用next()函数访问迭代器
描述器
可以描述一个属性操作的对象
# 描述器:可以描述一个属性操作的对象 属性的操作:增删改查
# 定义方式一:property(把方法封装给一个属性)
class Person:
def __init__(self):
self.__age = 10
def getAge(self):
return self.__age
def setAge(self, value):
if value < 0:
value = 0
self.__age = value
def delAge(self):
del self.__age
age = property(getAge, setAge, delAge) # age就是描述器,要在类里面实现set、get、del方法
P = Person() # 通过实例操作描述器,执行属性的增删改查
P.age = 19
print(P.age)
# P.delAge()
方法二:把相关一组操作封装的一个对象
# 描述器
class Age:
def __get__(self, instance, owner):
print("get")
def __set__(self, instance, value):
print("set")
def __delete__(self, instance):
print("delete")
class Person:
age = Age()
P = Person() # 通过实例操作描述器,执行属性的增删改查 不通过类
P.age = 19
print(P.age)
# P.delAge()
# 注意:
# 通过实例操作描述器,执行属性的增删改查 不通过类调用
# 类要是新式类,描述器在新式类失效
装饰器
# 使用类实现装饰器
def check(func):
def inner():
print("登录验证")
func()
return inner
def fass():
print("发说说")
fass = check(fass)
fass()
class check:
def __init__(self, func):
self.f = func
def __call__(self, *args, **kwargs):
print("登录验证")
self.f()
def fass():
print("发说说")
fass = check(fass)
fass() # 实例对象__call__才可以调用
生命周期方法
生命周期:对象从诞生到消亡
当一个对象被创建,会在内存中分配相应的内存空间进行存储
当一个对象不再使用,为了节约内存空间,会把这个对象释放
# 监听对象的生命周期
# __new__方法:当我们创建一个对象用于给对象分配内存的方法、通过拦截这个方法,可以修改对象的创建过程 比如单例设计模式
class Person:
# def __new__(cls, *args, **kwargs):
# print("新建了一个对象被我拦截了")
def __init__(self): # 创建实例的时候会自动调用这个方法
print("初始化方法")
self.name = "yl" # 通过self增加属性
def __del__(self): # 当对象被释放后会自动调用的方法
print("当对象被释放后会自动调用的方法")
pass
p = Person()
print(p)
# 小案例,创建实例计数加一,删除实例计数减一
class Person:
__personCount = 0 # 通过类名访问
def __init__(self): # 创建实例的时候会自动调用这个方法
print("创建实例计数加一")
self.__class__.__personCount += 1
def __del__(self): # 当对象被释放后会自动调用的方法
print("删除实例计数减一")
Person.__personCount -= 1
@staticmethod # 静态方法
def log():
print("当前人的个数是%d个" % Person.__personCount)
pass
p = Person()
p1 = Person()
Person.log()
del p
Person.log()
内存管理机制
引用计数器机制
垃圾回收机制
# 内存管理机制
# 可以通过id()函数获取内存地址(10进制)、通过hex()函数查看对应的16进制地址
# 垃圾回收方面:引用计数器、垃圾回收、特殊场景
# 引用计数器 一个对象会记录自身被引用的个数
class Person:
pass
p1 = Person()
p2 = p1 # 相当于p2 = Person()
import sys # 查看引用计数(要减一)
print(sys.getrefcount(p1)) # 结果会大一
# 引用计数加一场景:创建对象p1 = Person()、对象被引用p2 = p1、对象被作为参数传入到一个函数中getrefcount(p1)、对象作为一个元素存储在容器 l=[p1]
# 引用计数减一场景:对象别名被显示销毁del p1、对象别名被赋予新的对象p1 = 123、一个对象离开它的作用域(函数执行完毕)、对象所在容器被销毁或从容器中删除对象
# 无法解决循环引用问题
# 内存泄漏 一个对象用不了 但是还存在内存里面
垃圾回收机制底层实现原理
# 垃圾回收机制
# 从经历过引用计数器机制仍未被释放的对象中,找到循环引用删除相关对象
# 底层机制
# 1、收集所有的容器对象(可以引用其他对象的对象:元组、字典、列表、自定义类对象),通过双向链表进行引用
# 2、针对于每个容器对象,通过gc_refs来记录当前对应的引用计数
# 3、对于每个容器对象,找到他引用的容器对象,将这个容器对象的引用计数-1
# 经过步骤三,如果一个容器对象引用计数为0,代表可以回收,肯定是循环引用导致它活到现在
# 提升查找循环引用的性能
# 分代回收机制
# 1、默认一个对象创建出来属于0代
# 2、经历一代垃圾回收依然存活,则划分下一代
# 3、垃圾回收周期顺序:0代垃圾回收一定次数,会触发0代和1代一起回收、1代垃圾回收一定次数,会触发0代和1代和2代一起回收
# 查看和设置相关参数:
# 垃圾回收的时机
# 1、自动回收 开启垃圾回收机制并且达到了垃圾回收阈值
# (垃圾回收器中,增加的对象和释放对象个数之差达到某个阈值 gc.get_threshold()获取自动回收的阈值 gc.get_threshold(200,10,20)上设置自动回收阈值 才会触发)
# 200增加的对象和释放对象个数之差达到某个阈值 10 0代垃圾回收10次数,会触发0代和1代一起回收 20代垃圾回收20次数,会触发0代和1代和2代一起回收
# gc.enable() 开启垃圾回收机制(默认开启)
# gc.disable() 关闭垃圾回收机制
# gc.isenabled() 判定是否开启
# 2、手动回收
import objgraph
import gc
class Person:
def __del__(self):
print("对象释放")
pass
class Dog:
def __del__(self):
print("对象释放")
pass
p = Person()
d = Dog()
# 产生循环引用,通过引用计数器不可以释放。只有通过垃圾回收机制检测释放
p.pet = d
d.master = p
# 循环引用无法释放
del p
del d
# 打印当前垃圾回收器里面由Person产生的对象由多少个
print(objgraph.count("Person")) # 1
print(objgraph.count("Dog")) # 1
# 借助垃圾回收机制进行回收
gc.collect() # 收集所有垃圾
# 打印当前垃圾回收器里面由Person产生的对象由多少个
print(objgraph.count("Person")) # 0
print(objgraph.count("Dog")) # 0