Python-类实例化后的内存空间问题

本文探讨了Python中类和实例化对象的内存空间,类包含静态变量和方法,实例化后开辟独立内存存储绑定属性。类的命名空间包括静态变量和所有方法,而实例对象的命名空间仅包含绑定属性和类指针。实例通过类指针间接访问绑定方法。在处理静态属性时,对于可变对象可以直接修改,而对于不可变对象,看似修改实际上是在实例空间创建新属性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Python-类实例化后的内存空间问题

从数据结构和计算机基础知识的角度出发,一个新定义的类在编译阶段会创建一个类的命名空间,同时一个对象一旦被类实例化,也会开辟属于自己的内存空间;那它们之间的联系是什么,有哪些细节需要注意,下面跟着小涩一起去梳理。

类、实例化对象命名空间

直接上例子:

class A:
    Country = '中国'
    Area = 960
    def __init__(self,name,age,country):  # 这里多传一个参数也无所谓,只要没有赋值给self.属性就不开辟内存空间
        self.name = name
        self.age = age
    def func1(self,x):    # 实例对象绑定方法
        print(self)
        self.age += 1
    def func2(cls, n):  # 类方法/静态方法
		cls.Area += n
    def power(a,b):     # 一般方法
		return a ** b
    
if __name__ == "__main__":
    print(A.__dict__)  
    # 打印:{'__module__': '__main__', 'Country': '中国', 'Area': 1060, '__init__': <function A.__init__ at 0x00000160F53077B8>, 'func1': <function A.func1 at 0x00000160F5307840>, 'func2': <function A.func2 at 0x00000160F53078C8>, 'func3': <function A.func3 at 0x00000160F5307950>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
    a = A('alex',83,'印度')
    print(a.__dict_)
    # 打印:{'name': 'alex', 'age': 83}
    rst1 = A.func3(2, 5)  # 一般方法
    print(rst1)  # 32
    A.func2(A, 100)  # 类方法
    print(A.Area)  # 1060

显而易见,类的命名空间内存有除实例对象的属性外的所有内容,只要在类中有所定义,就会开辟空间存;即,

类的命名空间包括:静态变量/属性,定义到类中的所有方法(绑定方法、类方法、一般方法)

而目前看来实例对象,只包含其绑定的属性

那就有一个疑问,实例对象如何访问到它的绑定方法,因为绑定方法确实是放在类的命名空间。

类与实例对象的关联

python的底层源码大多基于C语言,查阅源码,实例对象之所以能访问他的绑定方法,其实用到了间接访问的原理,即C中的指针的概念;也就是说,

实例对象的命名空间包括:绑定的属性,类指针(用以指向类首元素的地址)。

如图所示,它们之间的关系:
关系图解

调用习惯与索引顺序

索引顺序

  • 对于属性/变量

    对象中的变量只属于对象本身,每个对象有属于自己的空间来存储对象的变量;如果自己没有就引用类的,如果类也没有就报错。对于类来说,类中的变量所有的对象都是可以读取的,并且读取的是同一份变量。

  • 对于方法(略,主要体现在子父类的继承上)

    说明一点,绑定方法虽然是存储在类的命名空间,但是对象能间接访问到,不存在这里所说的索引顺序。

调用的习惯

  • 方法

    • 类方法
      • 类名.类方法(类名,xx) # 类名作为参数不能丢
      • 对象.类方法(xx) # 无类名做参数,这种调用极少使用,容易出问题也没有实际意义,不建议。
    • 绑定方法:对象.绑定方法() # <==> 类名.绑定方法(对象)
    • 一般方法:类名.一般方法()
  • 属性/变量

    • 绑定属性: 对象.绑定属性

    • 静态属性

      • 类名.静态变量

      • 对象.静态变量

        针对能不能进行重新赋值的问题,很多书本上没有说明清楚,在此进行重点说明,见下一节说明

注意细节:类的静态属性

结论: 只要以对象打头对静态属性/变量进行写操作(赋值修改),不论是以对象.类方法的方式,还是对象.静态变量的方式;都要分两种情况具体分析

  1. 静态属性属于可变对象:列表,字典。

    可以进行写操作,真正意义上的对静态属性进行了写操作,与“类名.静态变量”这种调用方式等效。示例如下

    # 1
    class A:
        Country = ['中国']     # 静态变量/静态属性 存储在类的命名空间里的
        def __init__(self,name,age,country):  # 绑定方法 存储在类的命名空间里的
            self.name = name
            self.age = age
        def func1(self):
            print(self)
    
    a = A('alex',83,'印度')
    b = A('wusir',74,'泰国')
    a.Country[0] = '日本'
    print(a.__dict__)  # {'name': 'alex', 'age': 83},说明没有创建内存空间,
    print(a.Country) # ['日本']
    print(b.Country) # ['日本']
    print(A.Country) # ['日本']  静态变量被修改
    
  2. 静态属性属于不可变对象: 数值型、字符串、元祖、空None

    也可以进行写操作,符合语法,但是写了个寂寞,它是在对象命名空间上重新开辟了与静态属性同名的绑定属性并对其赋值。示例如下

    #2-1
    class A:
        Country = '中国'    # 静态变量/静态属性 存储在类的命名空间里的
        def __init__(self,name,age,country):  # 绑定方法 存储在类的命名空间里的
            self.name = name
            self.age = age
        def func1(self):
            print(self)
    
    a = A('alex',83,'印度')
    b = A('wusir',74,'泰国')
    a.Country = '日本'
    print(a.Country)  # 日本, 45行代码,使得对象a在自己空间中创建了Country同名的属性,实则上对类的静态变量没有修改成功
    print(b.Country)  # 中国,对象b没创建Country的属性空间,所以调用的还是类的静态变量Country
    print(A.Country)  # 中国
    
    #2-2
    class A:
        Country = ('中国', '香港')     # 静态变量/静态属性 存储在类的命名空间里的
        def __init__(self,name,age,country):  # 绑定方法 存储在类的命名空间里的
            self.name = name
            self.age = age
        def func1(self):
            print(self)
    
    a = A('alex',83,'印度')
    b = A('wusir',74,'泰国')
    # a.Country = ('日本','东京')
    print(a.__dict__)  # {'name': 'alex', 'age': 83, 'Country': ('日本', '东京')},说明在对象里创建内存空间,因为类的静态对象属于不可变对象-元祖
    print(a.Country) # ('日本', '东京')
    print(b.Country) # ('日本', '东京')
    print(A.Country) # ('日本', '东京')
    
### Python 实例化Python 中,实例化过程遵循特定的方式。当创建一个新对象时,实际上是调用了该的 `__new__` 方法来分配内存空间,随后通过 `__init__` 初始方法设置属性值[^2]。 对于来自不同模块内的多个,在主程序文件里可以通过导入相应模块并访问其中定义的来进行实例化操作: #### 实例化单个 假设有一个简单的定义如下所示: ```python # 定义在一个名为example_module.py 的文件中 class ExampleClass: def __init__(self, value): self.value = value print(f"{type(self).__name__} called with {value}") ``` 可以在另一个脚本中这样使用它: ```python from example_module import ExampleClass instance = ExampleClass("test") # 输出: "ExampleClass called with test" print(instance.value) # 输出: "test" ``` #### 动态加载和实例化 如果希望从字符串名称动态获取并对其进行实例化,则可以采用以下方式实现跨模块的对象创建: ```python import importlib modules_and_classes = [("a", "A"), ("b", "B")] instances = [] for module_name, class_name in modules_and_classes: module = importlib.import_module(module_name) cls = getattr(module, class_name) instance = cls() instances.append(instance) for inst in instances: print(type(inst)) ``` 这里假设有两个分别位于 `a.py` 和 `b.py` 文件里的 A 和 B ,上述代码会依次导入这两个模块,并根据给定的名字找到对应的完成实例化工作[^1]。 需要注意的是,实际应用中应当确保所涉及路径已加入到系统的 PYTHONPATH 环境变量或者当前工作目录下以便顺利定位目标模块。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值