面试复习总结day01
目录一.python可变与不可变数据类型
1.1可变数据类型
不可变数据类型在声明赋值的时候 会在内存中开辟一块内存空间,用来存放这个变量被赋的值,而这个变量实际上存储的,并不是被赋值的这个值,而是存放这个值所在空间的内存地址,通过这个地址,变量就可以在内存中取出数据了。
所谓不可变数据类型: 我们不能改变这个数据在内存中的值,所以当我们该案这个变量的赋值时,只是在内存中重新开辟了一块新的内存空间,将这一条新的数据存放这一个新的内存地址里,而原来的变量就不再引用原数据的内存地址而转为新数据的内存地址
举例:
x = 18
id(x) # 4497811200
id(18) # 4497811200
x = 19
id(x) # 4497811232
id(18) # 4497811200
y = 18
id(y) # 4497811200
1.2可变数据类型
结合不可变数据类型,可变数据类型就是指变量所指向的内存地址的值时可以被改变的
x = [1, 2, 3]
id(x) # 4501838920
y = [1, 2, 3]
z = [1, 2, 3]
id(y) # 4501838600
id(z) # 4501838664
1.3 哪些是可变数据类型
可变:Set(集合)、List(列表)、Dictionary(字典)
值改变 内存地址不变
# 集合Set
s = {1, 'd1', '334', '1', 1}
print(s, id(s), type(s))
s.add('wei')
print(s, id(s), type(s))
# {1, '1', '334', 'd1'} 1618243922848 <class 'set'>
# {1, '334', 'wei', 'd1', '1'} 1618243922848 <class 'set'>
# 列表
l = [1, 'q', 'wqeq', True]
print(l, type(list), id(list))
l.append('wei')
print(l, type(list), id(list))
# [1, 'q', 'wqeq', True] <class 'type'> 140703227148336
# [1, 'q', 'wqeq', True, 'wei'] <class 'type'> 140703227148336
# 字典
tuple = (1)
d1 = {1: 2}
d = { tuple:1,'key2':'wei','key3':'111'}
print(d,type(d), id(d))
d['key4'] = 'hahaha'
print(d,type(d), id(d))
# {1: 1, 'key2': 'wei', 'key3': '111'} <class 'dict'> 1898762162688
# {1: 1, 'key2': 'wei', 'key3': '111', 'key4': 'hahaha'} <class 'dict'> 1898762162688
1.4 哪些是不可变数据类型
不可变:Number(数字)、String(字符串)、Tuple(元组)
值改变 内存地址也改变
# 整形
a = 1
print(id(a), type(a))
a = 2
print(id(a), type(a))
# 140703227381408 <class 'int'>
# 140703227381440 <class 'int'>
# 字符串
b = 'wei'
print(id(b), type(b))
b = 'wei11'
print(id(b), type(b))
# 2070582654064 <class 'str'>
# 2070577872368 <class 'str'>
# 元组
c1 = ['1', '2']
c = (1, 2, c1)
print(c, id(c), type(c))
c1[1] = 'djx'
print(c, id(c), type(c))
# (1, 2, ['1', '2']) 2226963360704 <class 'tuple'>
# (1, 2, ['1', 'djx']) 2226963360704 <class 'tuple'>
ps: 元组的值发生了变化而内粗地址没变,但元组依然是不可变类型 元组被称为只读列表,即数据可以被查询,但不能被修改
1.5 Tuple元组小知识补充:
数据类型之元组:tuple
元组可以说是小括号组成的列表,中间的数据同样是用逗号隔开,数据类型可以是任意的。同时元组也可以索引取值,但是元组中绑定的内存地址不能修改,如果绑定的是一个列表,我们依旧可以修改列表中的数据值
example = (11, 22, 33, 44, [12, 23, 34, 'name'])
通过运行代码,我们发现修改其他元组内的数据值会直接报错,但是修改列表内的数据值是可以的。
example = (11, 22, 33, 44, [12, 23, 34, 'name'])
example[4][0] = 222
print(example)
# (11, 22, 33, 44, [222, 23, 34, 'name'])
特殊形况:
当元组内只有一个数据值时,需要在小括号内的数据值后跟上一个逗号,否则不是数据类型不是元组,会变成你缩写的数据对应的类型。
example1 = (11,) # 是tuple类型
example2 = (11) # 是int类型
列表和元组的区别
列表是动态数组,它们可变且可以重设长度(改变其内部元素的个数)。
元组是静态数组,它们不可变,且其内部数据一旦创建便无法改变。
元组缓存于Python运行时环境,这意味着我们每次使用元组时无须访问内核去分配内存。
这些区别结实率两者在设计哲学上的不同:
列表可被用于保存多个互相独立对象的数据集合
元组用于描述一个不会改变的事务的多个属性
1.6 数据类型值集合:Set
集合跟数学中的集合定义相似,用大括号组成,中间插入数据值并用逗号隔开,但是不想字典需要kv键值对形式表示
集合多用于去重和关系运算,只能是不可变类型,空集合需要用特殊的方法定义,否则会显示成字典类型。
s1 = {}
s2 = set()
print(type(s1)) # <class 'dict'>
print(type(s2)) # <class 'set'>
二、python垃圾回收机制
2.1机制讲解
运行程序时候,很多数据会被闲置,得不到使用,这时就需要把这些无用的数据清除,一些语言需要自己编写代码删除。python中不需要这么做,只需要了解python中垃圾回收机制的原理和运行方式
2.2 引用计数
一个对象被引用时,引用计数加1,当对象被del时,引用计数减1,为0时,对象就被清除,一般情况下用户不会去操作python的垃圾回收机制 但它留有API接口
2.3 循环引用(标记清除)
当学习了引用计数后我们可以得知每次与变量名绑定,数据值都会增加一次引用计数,这里我们可以举个例子来说明循环引用:
s1 = [1,2,3]
s2 = [4,5,6]
#当我们在s1和s2中互相加入,可以看到引用计数都编程了2。
s1.append(s2)
s2.append(s1)
#但是当我们把s1、s2与数据值的绑定关系断开后
我们可以发现两个列表的引用计数变成了1。
当这种情况出现的时候,数据值并不会被第一时间被清理掉,而是会一直存在于内存空间中,直到内存空间占满,程序会在这是自动停止运行,然后扫描所有的程序并给循环引用的程序打上标记,之后一次性清除。(这种处理方式叫标记清除)
2.4 分代回收
分代回收就是垃圾回收机制运行时,不对所有目标进行频繁检测,对于使用频率高的目标降低检测频率。
当python运行垃圾回收机制的时候,如果频繁运行,同样会占用大量的内存资源。这个时候就体现了分代回收的用处。
三、进程 线程 协程
3.1 进程
- 操作系统进行资源分配和调度的基本单位,多个进程之间相互独立
- 稳定性好,如果一个进程崩溃,不影响其他进程,但是进程资源消耗大,开启进程的数量有限
3.2 线程
- CPU进行资源分配的调度的基本单位,线程时进程的一部分,是比进程更小的能独立运行的基本单位,一个进程下的多个线程可以共享该进程的多有资源
- 如果IO操作密集,则可以多线程运行效率高,缺点是一个线程崩溃,都会造成进程的崩溃
3.3 协程
- 子程序调用总是一个入口,一次返回,调用顺序是明确的,而协程的调用和子程序不同。
- 携程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。
四、关键字with和上下文管理
4.1 with基本语法
with open('output', 'w') as f:
f.write('Hello world')
上面的代码往output文件写入了Hello world字符串,with语句会在执行完代码块后自动关闭文件。这里无论写文件的操作成功与否,是否有异常抛出,with语句都会保证文件被关闭。
如果不用with,我们可能要用下面的代码实现类似的功能
try:
f = open("output", "w")
f.write("Hello world")
finally:
f.close()
一种更加简洁、优雅的方式就是用 with 关键字。open 方法的返回值赋值给变量 f,当离开 with 代码块的时候,系统会自动调用 f.close() 方法, with 的作用和使用 try/finally 语句是一样的。那么它的实现原理是什么?在讲 with 的原理前要涉及到另外一个概念,就是上下文管理器(Context Manager)。
上面的with代码背后发生了些什么?我们来看下它的执行流程
- 首先执行open('output', 'w'),返回一个文件对象
- 调用这个文件对象的__enter__方法,并将__enter__方法的返回值赋值给变量f
- 执行with语句体,即with语句包裹起来的代码块
- 不管执行过程中是否发生了异常,执行文件对象的__exit__方法,在__exit__方法中关闭文件。
这里的关键在于open返回的文件对象实现了__enter__和__exit__方法。一个实现了__enter__和__exit__方法的对象就称之为上下文管理器。
4.2 上下文管理器
上下文管理器定义执行 with 语句时要建立的运行时上下文,负责执行 with 语句块上下文中的进入与退出操作。__enter__方法在语句体执行之前进入运行时上下文,__exit__在语句体执行完后从运行时上下文退出。
在实际应用中,__enter__一般用于资源分配,如打开文件、连接数据库、获取线程锁;__exit__一般用于资源释放,如关闭文件、关闭数据库连接、释放线程锁。
4.3 自定义上下文管理器
enter() - 进入上下文管理器的运行时上下文,在语句体执行前调用。如果有as子句,with语句将该方法的返回值赋值给 as 子句中的 target。
exit(exception_type, exception_value, traceback):
退出与上下文管理器相关的运行时上下文,返回一个布尔值表示是否对发生的异常进行处理。
如果with语句体中没有异常发生
则__exit__的3个参数都为None,即调用 exit(None, None, None),并且__exit__的返回值直接被忽略。如果有发生异常,则使用 sys.exc_info 得到的异常信息为参数调用__exit__(exception_type, exception_value, traceback)
出现异常时,如果__exit__(exception_type, exception_value, traceback)返回 False,则会重新抛出异常,让with之外的语句逻辑来处理异常;如果返回 True,则忽略异常,不再对异常进行处理。
理解了__enter__和__exit__方法后,我们来自己定义一个简单的上下文管理器。这里不做实际的资源分配和释放,而用打印语句来表明当前的操作。
class Context(object):
def __enter__(self):
print('[in __enter__] 获取资源')
def __exit__(self, exc_type, exc_val, exc_tb):
print('[in __exit__] 释放资源')
if exc_type is None:
print('[in __exit__] 没有异常退出')
else:
print('[in __exit__] 异常退出: %s' % exc_val)
return False
with Context():
print('[in with-body] 正在测试')
运行上面的代码 得到的结果:
[in __enter__] 获取资源
[in with-body] 正在测试
[in __exit__] 释放资源
[in __exit__] 没有异常退出
我们在with语句体中人为地抛出一个异常
with Context():
print('[in with-body] 正在测试')
raise (Exception("something wrong"))
会得到如下的输出
in __enter__] 获取资源
[in with-body] 正在测试
[in __exit__] 释放资源
[in __exit__] 异常退出: something wrong
Traceback (most recent call last):
File "D:/复习/2.py", line 18, in <module>
raise (Exception("something wrong"))
Exception: something wrong
Process finished with exit code 1
标签:__,01,复习,数据类型,元组,面试,exit,print,id
From: https://www.cnblogs.com/wei0919/p/17129727.html