Python面向对象中类属性与对象属性的内存关系

Python面向对象中类属性与对象属性的内存关系,第1张

Python面向对象中类属性与对象属性的内存关系 Python面向对象中类属性与对象属性的内存关系

参考教程:【Python】零基础入门——面向对象编程(强烈推荐)_哔哩哔哩_bilibili

前言

看到这个标题,我觉得大部分人是奇怪的,因为在我之前的想法中,我一直觉得类是模板,是不占内存的,但在接触了python以后,我觉得类其实也是一个对象,它也是占了内存的。

顺便提一句,在python中,类名是可以直接调用属性,例如:

class Student:
    pass
print(Student.__name__)

类名与类之间的关系

而且跟之前学C++一样,函数名是一个指针,也就是函数的地址,所以类名其实也是一个指针,或者叫做地址ID号,所以我们看到类与类名的关系可以看成如下的关系:

对象属性/对象与类的基本内存关系

而在python中,对象里面其实存在了一个名为class的属性指向了对象本身,图示如下:

有意思的是python面向对象中对象的属性,首先在python当中,对象的属性是可以后期加的,例如如下这种情况:

class Student:
    pass
ZhangSan = Student()
ZhangSan.age = 18
# 查看对象里面的所有属性
print(ZhangSan.__dict__)
# 同时也可以删除属性
del p.age
# 修改看起来没有啥区别,只需要直接修改即可,但内存 *** 作也还是很有意思,而且如果属性已经存在了才叫修改,不然该 *** 作就表示为添加属性
print(id(ZhangSan.age))
ZhangSan.age = 21
print(id(ZhangSan.age))
# 这里会发现这里的ID号是不一样的
# 但值得注意的是列表修改是不会存在修改内存的地址的,也就是如下两个ID号并没有发生改变,以下名字都是乱取的,如有雷同,还请见谅
ZhangSan.Former_girlfriend["吴琴","徐四"]
ZhangSan.Former_girlfriend["吴琴","徐四","冯安康"]

而更有意思的是python对象添加属性的内存的 *** 作,它并不是将属性直接添加到自己的内存里面,而是另外再开一片内存,用来存这个属性值,然后保存好这个属性值的ID号(地址),图示如下:

对象属性修改的内存图示如下,python不会直接修改原内存的值,而是直接再开一块内存,将对象内属性对应的地址(ID号)进行修改,(其中18没有其他引用情况下,会被释放掉)图示如下:

对象属性被删除时的内存 *** 作图示如下(18的内存是否释放,主要看有没有其他变量引用它,没有会一起释放掉):

对象内存里面一些好玩的东西

首先第一个好玩的东西就是,从上面我们可以知道我们是可以后天增加属性的,但是对象1增加了一个属性,不代表对象2也增加一个这个属性,如果强行去访问就会报错,例如:

class student:
    pass
ZhangSan = student()
ZhangSan.age = 18
LiSi = student()
LiSi.name = "LiSi"
# 在这里之前都没错,但如果我访问张三的name就会出错了,因为我并没有给张三加上name的属性,所以就会报错

类属性的内存关系

由于篇幅问题,这里我仅介绍类与对象属性之间的不同,与我之前学其他语言不同,python的类也可以看作为一个对象,所以类的属性也是可以直接使用的

类属性跟对象一样都是用ID号(指针)的方式存储在类存储里,其中值得关注的点就是对象也可以访问类属性,例如:

class Student:
    age = 18
    grade = 'A'
    pass
ZhangSan = Student()
print(ZhangSan.age)

这里乍一看跟其他对象的访问都没有问题,但如果说你要修改它的值,这里就存在说法啦,而且反映到内存里面,对象首先通过__class来访问到类,然后再通过类来访问类属性值,而不是把这三个属性放到对象的内存里面或则和重开一块内存,也就是说多个对象一开始访问到的都是类属性的值,不是各自对象的值关系图如下所示:

python对象的属性查找机制

通过上面的例子,就不得不说python对象的属性访问顺序了,一般来说对象的属性会首先从自身开始找起,如果自身没有,则会从类里面开始找,如果类里面还没有,会从父类里面开始找,一直找到最后发现没有,就会报错,即这个属性不存在。这里通过下面的例子就可以发现(但注意不可以通过对象来进行增加和修改,同理删除也是)

class Student:
    age = 18
    grade = 'A'
    pass
class person:
    sex = 'man'
    pass

ZhangSan = Student()
ZhangSan.__class__ = person
# 则这里访问age就会报错
print(ZhangSan.age)
#这里会成功打印出来
print(ZhangSan.sex)
# 这里也可以通过删除报错来验证
class Student:
    age = 18
    grade = 'A'
    pass
ZhangSan = Student()
# 这里会报一个错误,会发现,类在这里是一个对象
del Student.age
print(Student.age)
# 这里会报一个对象没有age的错误
del ZhangSan.age

python对象属性与类属性相同时会增加内存

这个时候你一定会问了,如果我有多个对象,都访问这个属性,是不是都访问到类属性里面?那我可以准确地告诉您,如果你没有修改过这个值,答案是:是的

但如果你修改这个属性值,那么对象会重新开一块内存,图示如下:

类属性中的dict和对象中的dict

与之前的说法进行一点点小小的修改,在python当中其实是存在一个dict属性的,所以其实对象或者类的属性其实是保存在一个叫做dict属性的键值对里面,然后与对象不同的是类的dict属性是只能读不能写的,在如下的例子可以验证

class Student:
    age = 18
    grade = 'A'
    pass
ZhangSan = Student()
print(ZhangSan.__dict__)
# 或者如下方式
ZhangSan.__dict__ = {"name": "ZhangSan","age": 21}
print(ZhangSan.__dict__)
# 或者
ZhangSan.age = 21
print(ZhangSan.__dict__)

思考

看到上面那一部分,那么下面这个程序输出的值就不言而喻了,答案分别是:

class Student:
    age = 18
    grade = 'A'
    pass
ZhangSan = Student()
ZhangSan.age += 3
# ZhangSan.age = ZhangSan.age +
# 输出 21
print(ZhangSan.age)
# 输出 18
print(Student.age)

怎么限制python对象不可以随意增加属性

# 可以加关键字__slot__ 用于限制添加属性
class Student:
    age = 18
    grade = 'A'
    __slot__ = ["age","grade"]
    pass
# 这样就限制只能添加age grade这两个属性

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5679932.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存