Python入门
1.变量
- 变量就是可变的的量,用来描述某个事物的属性。本质作用就是描述和接收变量值
- 定义变量的方式:
变量名 = 变量值
-
变量名的规则:
- 变量名必须具有描述意义
- 变量名以字母、数字和下划线组成。
- 变量名不能以关键字命名
-
变量名的两种表示方式:
下划线和驼峰体(以解决多个单词组成变量名的问题)
-
打印变量的3种方式
x = 257
print(x,id(x),type(x))
#分别打印x的值,值的内存地址,值的数据类型
- 变量的概念是由Python提供的,如果不运行程序代码,就没有变量
2. Python程序的两种方式
(1)交互式
-
优点:写一行翻译一行,能够及时发现bug
-
缺点:执行繁琐,消耗时间
(2)命令行式
-
优点:执行效率高
-
缺点:找bug困难
3. Python垃圾回收机制
- 引用计数:python中定义的变量值都有一个变量名引用这个值,每有一个变量名引用它,这个变量值的引用计数就会加一,当引用计数为零时,这个变量值就会发回收,对应的内存空间也会被回收。
- 总体来说,在Python中,主要通过引用计数进行垃圾回收;通过 “标记-清除” 解决容器对象可能产生的循环引用问题;通过 “分代回收” 以空间换时间的方法提高垃圾回收效率。
- python的GC(garbage collection)以引用计数为主,标记清除和分待回收为辅来进行垃圾回收。
(1)python垃圾回收机制详解
- 垃圾回收机制
垃圾回收机制(简称GC)是Python解释器自带的一种机制,专门用来回收不可用的变量值所占用的内存空间
- 作用
程序运行过程中会申请大量的内存空间,而对于一些无用的内存如果不及时清理的话会导致内存溢出,导致程序崩溃,python解释器自带的垃圾回收机制把程序员从繁杂的内存管理中解放出来
- 原理分析
Python的GC模块主要运用了‘引用计数’来跟踪和回收垃圾。在引用计数的基础上,还可以通过’标记清除‘解决容器对象可能产生的循环引用的问题,并且通过’分代回收‘以空间换时间的方式来进一步提高垃圾回收的效率
- 引用计数
引用计数就是变量值被变量名关联的次数
引用计数增加的情况:
- 对象被创建
- 对象被引用
- 对象作为参数,传入到一个函数中
- 对象作为一个元素,存储在容器中
引用计数减少的情况:
- 对象的别名被显式销毁(例:del a)
- 对象的别名被赋予新的对象
- 对象离开它的作用域,例如函数执行完毕时,函数中的局部变量(全局变量不会)
- 对象所在的容器被销毁,或从容器中删除对象
对象的引用计数一旦变为0,其占用的内存地址就应该被解释器的垃圾回收机制回收
- 缺点
变量值被关联次数的增加或减少,都会引发引用计数机制的执行,存在明显的效率问题
- 循环引用问题
# 如下我们定义了两个列表,简称列表1与列表2,变量名l1指向列表1,变量名l2指向列表2
>>> l1=['xxx'] # 列表1被引用一次,列表1的引用计数变为1
>>> l2=['yyy'] # 列表2被引用一次,列表2的引用计数变为1
>>> l1.append(l2) # 把列表2追加到l1中作为第二个元素,列表2的引用计数变为2
>>> l2.append(l1) # 把列表1追加到l2中作为第二个元素,列表1的引用计数变为2
# l1与l2之间有相互引用
# l1 = ['xxx'的内存地址,列表2的内存地址]
# l2 = ['yyy'的内存地址,列表1的内存地址]
>>> l1
['xxx', ['yyy', [...]]]
>>> l2
['yyy', ['xxx', [...]]]
>>> l1[1][1][0]
'xxx'
循环引用会导致:值不再被任何名字关联,但是值的引用计数不为0,应该被回收但不能被回收
>>> del l1 # 列表1的引用计数减1,列表1的引用计数变为1
>>> del l2 # 列表2的引用计数减1,列表2的引用计数变为1
此时只剩下列表1和列表2之间的相互引用,两个列表的引用计数均不为0,但是两个列表不再被任何其他对象关联,没有任何人可以再引用到他们,所以他们占用的内存空间应该被回收,但是由于相互引用的存在,每个对象的引用计数都不为0,所以他们占用的内存永远都不会被释放,
所以,Python引入了‘标记清除‘和’分代回收’来分别解决引用计数的循环引用与效率低的问题
- 标记清除
解决容器类对象可能产生的循环引用问题(注意:只有容器类对象才会产生循环引用的情况,比如列表、字典、用户自定义类的对象、元组等;像数字字符串这类简单类型不会出现循环引用;作为一种优化策略,对于只包含简单类型的元组,也不在标记清除算法的考虑之列)
在python中,关于变量的存储,内存中有两块区域:堆区和栈区,在定义变量时,变量名与值内存地址的关联关系存放在栈区,变量值存放于堆区,内存管理回收的是堆区的内容
当定义行x=10,y=20时,
当执行x=y时,
标记清除算法的做法是当应用程序可用的内存空间被耗尽时,就会停止整个程序,然后进行两项工作,一是标记,二是清除
#1、标记
标记的过程其实就是,遍历所有的GC Roots对象(栈区中的所有内容或者线程都可以作为GC Roots对象),然后将所有GC Roots的对象可以直接或间接访问到的对象标记为存活的对象,其余的均为非存活对象,应该被清除。
#2、清除
清除的过程将遍历堆中所有的对象,将没有标记的对象全部清除掉。
直接引用指的是从栈区出发直接引用到的内存地址,间接引用指的是从栈区出发引用到堆区后再进一步引用到的内存地址
这样在启用标记清除算法时,发现栈区内不再有l1与l2(只剩下堆区内二者的相互引用),于是列表1与列表2都没有被标记为存活,二者会被清理掉,这样就解决了循环引用带来的内存泄漏问题。
- 什么是内存泄漏
指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内存在物理上的
消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪
费。导致程序运行速度减慢甚至系统崩溃等严重后果。
有 del() 函数的对象间的循环引用是导致内存泄漏的主凶。
不使用一个对象时使用:del object 来删除一个对象的引用计数就可以有效防止内存泄漏问题。
通过 Python 扩展模块 gc 来查看不能回收的对象的详细信息。
可以通过 sys.getrefcount(obj) 来获取对象的引用计数,并根据返回值是否为 0 来判断是否内存
泄漏。
- 分代回收
基于引用计数的回收机制,每次回收内存,都需要把所有对象的引用计数都遍历一遍,这是非常消耗时间的,于是引入了分代回收来提高回收效率,分代回收采用的是用“空间换时间”的策略。
分代指的是根据存活时间来为变量划分不同等级(也就是不同的代)
新定义的变量,放到新生代这个等级中,假设每隔1分钟扫描新生代一次,如果发现变量依然被引用,那么该对象的权重(权重本质就是个整数)加一,当变量的权重大于某个设定得值(假设为3),会将它移动到更高一级的青春代,青春代的gc扫描的频率低于新生代(扫描时间间隔更长),假设5分钟扫描青春代一次,这样每次gc需要扫描的变量的总个数就变少了,节省了扫描的总时间,接下来,青春代中的对象,也会以同样的方式被移动到老年代中。也就是等级(代)越高,被垃圾回收机制扫描的频率越低
缺点:
例如一个变量刚刚从新生代移入青春代,该变量的绑定关系就解除了,该变量应该被回收,但青春代的扫描频率低于新生代,所以该变量的回收就会被延迟。
4. 简述Python小整数池的概念
-
概念:
Python小整数池是当Python启动时,会自动定义 [-5,256]之间的整数变量,它们的内存地址已经被写死,任何变量引用这个区间内的整数时,内存地址都不会改变
-
对于x =10,请用Python代码分别打印变量值、变量值的内存地址、变量值数据类型
x=10 print(x,id(x),type(x)) # 打印结果:10 1490972416 <class 'int'>
-
下述代码,请判断x、y、z的变量值是否相同?x、y、z的所在的内存地址是否相同,请用Python代码阐述为什么?
x = 257 y = x z = 257 print(x,y,z)#打印x、y、z的值 #打印结果:257 257 257 print(id(x),id(y),id(z))#打印x、y、z值的内存地址 #打印结果:5811920 5811920 5811920
答:x、y、z的值相同,x、y的内存地址相同,z与x、y的内存地址不同,因为x、y的引用都是同一个值,而z = 257 定义了一个新的变量,z引用的是另一个值。所以x、y的内存地址相同,z与x、y的内存地址不同。
5. 简述数字类型
1.数字类型分为整型和浮点型
(1)整型 ——》int
- 整型就是整数类型,可以将浮点型的值强制转换成整数的类型,但不会四舍五入,想四舍五入就要用 round() 。
salary2 = int(3.7)
print(salary2)
#打印结果:3 不会四舍五入
print(round(salary2))
#打印结果:4 会四舍五入
round()会把括号里的浮点型四舍五入
- 使用方法: + - * / % // **
(2)浮点型 ——》float
-
浮点型就是小数类型的数,可以用float将整型强制转换成浮点型的值。
height = float(4) # 强制类型转换 print(height) #打印结果:4.0
-
使用方法: + - * / % // **
6. 简述字符串类型
1.定义字符串的方式:
- 单引号 (字符串里不能有单引号)
- 双引号 (字符串里不能有双引号)
- 三单引号 (字符串里不能有三单引号)
- 三双引号 (字符串里不能有三双引号)
2.连接字符串
- python中,不推荐使用 + 来连接字符串,因为效率太低,推荐使用格式化的格式连接字符串。
str1 = 'nick'
str2 = 'handsome'
print(str1 +' ' + str2 ) # 字符串不能和数字相加
#打印结果:nickhandsome
3.输出n次字符串
str1 = 'nick'
print(str1 * 5)#相当于乘法,打印5次字符串,中间会用空格隔开
#打印结果:nick nick nick nick nick