Python初学者条记第六期 -- (变量与函数内存分析)

打印 上一主题 下一主题

主题 1723|帖子 1723|积分 5173

13、 变量与函数内存分析

1.Python中变量的本质



  • 静态编译型语言:C、C++、Java

    • 编译型:源代码不能直接交给盘算机运行,必须先编译,生成可实行的二进制文件,再将该二进制文件交给盘算机实行。C/C++编译的二进制效果 .exe文件,Java编译的二进制效果 .clss文件。【将一本英语书翻译为中文书】
    • 静态:变量的界说必须声明数据类型,而且基本数据类型的数据是直接存储在变量内部的!(C\C++的指针和Java中的引用数据类型除外)

  • 动态解释型语言:Python、JS

    • 解释型:源代码依旧不能直接交给盘算机运行,也是必须先编译,但是可以不生成二进制文件,二进制的代码在内存中临时存储的,运行的时候就是将二进制代码按照编译的顺序依次实行。【同声传译】
    • 动态:变量的界说不须要声明数据类型,数据存储在堆内存中的,变量仅存储数据对象在堆内存中的所在而已。

  1. # Java
  2. public class Demo {
  3.     public static void main(String[] args) {
  4.         int a = 1231231231; //范围没超
  5.         int b = 100;        //范围没超
  6.         int c = a * b;      //暂时不确定 只有运行的时候才能确定大小
  7.         // 整型溢出
  8.         System.out.println(a * b);
  9.     }
  10. }
  11. # C
  12. #include<stdio.h>
  13. #include<stdio.h>
  14. void main(){
  15.     // overflow in implicit constant conversion
  16.     // 存不下了
  17.     int a = 123123123123;
  18.     printf("%d\n", a);
  19. }
复制代码
  1. num = 1
  2. print(num)
  3. print(type(num)) # <class 'int'>
  4. print(id(num))   # 140731596990904
  5. num = 3.14
  6. print(num)
  7. print(type(num)) # <class 'float'>
  8. print(id(num))   # 1998567916656
  9. num = "Hello"
  10. print(num)
  11. print(type(num)) # <class 'str'>
  12. print(id(num))   # 1998567809472
  13. xixi = num
  14. # 假设xixi和num都嗝屁了 上述三个数据对象怎么办?
  15. # 没有被任何变量指向的对象,则成为垃圾,则被GC垃圾回收器自动回收
复制代码
  1. print("Hello")
  2. haha = print
  3. haha("World")
  4. print = 1
  5. haha(1 + 2)
  6. haha(print)
  7. print(1 + 2) # TypeError: 'int' object is not callable
  8. """
  9. 使用type()查看类型 id()查看地址
  10. """
复制代码
总而言之,在Python中,变量(包含函数名)划一存储的数据对象在堆内存中的所在!
  1. def show(a, b):
  2.     print(a + b)
  3.     return
  4. test = show
  5. print(test(1,2))
复制代码
2.常量驻留问题

常量驻留是一种优化机制,它允许Python在内存中之生存一份特定值的副本,而多个变量可以引用这个相同的副本。这样做的目标,为了节省内存空间,并提高某些操作的效率。


  • 小整数驻留问题
  1. >>> a = 1
  2. >>> b = 1
  3. >>> a == b # 指向的对象内容是否一致
  4. True
  5. >>> a is b # 指向的是否是同一个对象
  6. True
复制代码
解释:a变量和b变量存储的是数据对象1的内存所在,只有一个1的数据对象。
  1. >>> a = 300
  2. >>> b = 300
  3. >>> a == b
  4. True
  5. >>> a is b
  6. False
复制代码
解释:a变量和b变量存储的是数据对象300的内存所在,有两个300的数据对象。
Python会为范围在[-5,256]之间的整数自动进行常量驻留。这就意味着,当你创建多个值在次范围内的话,他们实际上引用的是同一个数据对象。
上述的演示,是在控制台交互模式下的效果
在脚本模式下,有体现出了不一样的效果:
  1. a = 1
  2. b = 1
  3. print(a == b)
  4. print(a is b)
  5. a = 300
  6. b = 300
  7. print(a == b)
  8. print(a is b)
  9. """
  10. True
  11. True
  12. True
  13. True
  14. """
复制代码
脚本当中,两个300是同一个数据对象,为什么呢?因为会有潜在的优化问题。
  1. >>> id(-5)
  2. 140732088707320
  3. >>> id(-4)
  4. 140732088707352
  5. >>> id(-3)
  6. 140732088707384
  7. >>> id(254)
  8. 140732088715608
  9. >>> id(255)
  10. 140732088715640
  11. >>> id(256)
  12. 140732088715672
  13. >>> id(300)
  14. 1267470938224
  15. >>> id(300)
  16. 1267470938224
  17. >>> num = 400
  18. >>> id(num)
  19. 1267473808912
  20. >>> num2 = 400
  21. >>> id(num2)
  22. 1267473808880
复制代码
可以看到,自带驻留常量的所在是连续的,重新创建的再范围之外的常量,所在有可能连续也有可能不连续,但是跟常量驻留的所在连续吗?反证出300、400是新建的。
  1. a = 300
  2. b = 300
  3. print(id(a))
  4. print(id(b))
  5. """
  6. 3110779789776
  7. 3110779789776
  8. """
复制代码


  • 字符串驻留问题
对于字符串而言,Python也会对一些符合特定条件的字符串进行常量驻留,字符串常量只能包含字母、下划线、数字,而且在编译期间就能确定其值的字符串。
  1. >>> s1 = "HelloWorld"
  2. >>> s2 = "HelloWorld"
  3. >>> s1 == s2
  4. True
  5. >>> s1 is s2
  6. True
复制代码
由于下面的字符串当中出现了空格,所以则不会进行常量驻留,就是两个字符串对象,只不外内容相同而已。
  1. >>> s1 = "Hello Python"
  2. >>> s2 = "Hello Python"
  3. >>> s1 == s2
  4. True
  5. >>> s1 is s2
  6. False
复制代码
s1的值在代码编辑期间,甚至到编译的时候,它的值就已经确定了,s2的值只有程序实行的时候才能确定。虽然s1和s2最终的内容是一样的,但是s2是运行期间创建的,它是别的一个数据对象。
  1. >>> s1 = "HelloWorld"
  2. >>> a = "Hello"
  3. >>> b = "World"
  4. >>> s2 = a + b
  5. >>> s1 == s2
  6. True
  7. >>> s1 is s2
  8. False
复制代码
同样,在脚本环境中,效果又有差异的表现。
3.函数在内存中的运行逻辑

函数的运行是基于栈内存的,栈就是一个先辈后出的线性表
我们可以把一个函数当成是栈当中的一个元素:栈帧 -> 函数本身须要占用的内存
都包含哪些内容呢:


  • 函数名-引用关系
  • 参数列表
  • 函数内容:函数体 return 返回值
接着来看,具体的运行机制是这样的:
(1)函数被调用时,会从堆内存中将函数的代码加载进栈内存:进栈
(2)哪个函数在栈顶,哪个函数就有限实行
(3)直到栈顶函数碰到return时,竣事函数并将返回值传递给调用者:出栈
(4)在栈顶函数运行期间,如果又调用了其他函数,则当前函数停息运行,直到成为新的栈顶则继续实行。
  1. def pow(a, b):
  2.     c = sum(a,b) ** b
  3.     return c
  4. def sum(a,b):
  5.     c = a + b
  6.     return c
  7. a = 1
  8. b = 2
  9. ret = pow(a,b)
  10. print(ret)
复制代码

总结:


  • 局部变量是随着函数的进栈而创建的,随着函数的出栈而灭亡
  • 全局变量是随着程序的实行而创建的,随着程序的技能而灭亡
  1. num = 10
  2. def show():
  3.     # 函数内部寻找变量或函数的逻辑:就近原则
  4.     print(num)
  5. show()
复制代码
一般不建议在函数中直接使用全局变量,而是作为参数进行传递,为啥?一旦改变全局变量的值,那么直接调用该变量的函数在运行时就会出现有任务逻辑问题。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

东湖之滨

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表