Python基础复习1
Python专题
- Python赋值操作
代码:
a = 1 # 内存会为1创建一个数据单元,再让a指向这个单元
print(id(a)) # 1940417552
a = 2 # 内存会为2重新创建一个数据单元,再让a重新指向这个单元
print(id(a)) # 1940417584
- a变量指向的内存地址发生变化
- Python是直接操控数据,数据处于主动地位,变量只是作为一种引用的关系存在,而不再拥有存储的功能
- “数据在python中称之为对象”,对象不能够被覆盖,也不能被销毁(垃圾回收机制会来回收不用的对象)
- 对象分类:
- 不可变对象:int str float tuple
- 可变对象:dict set list
- 可变不可变指的是:指向是否可变
- 不可变对象:对象存放地址中的值会改变
- 具有相同值的不可变对象是同一个对象
- 只要不可变对象指向的值发生变化,不可变对象就会重新指向新的值
- 可变对象:对象存放地址中的值不会改变(即重新创建地址存放新对象)
- 具有相同值的可变对象是不同的对象
- 允许同一对象中的值发生变化,但是首地址不会发生改变
- Python列表和元组比较
- tuple 元组是一个只读版本的 list 列表
- tuple 元组初始化效率要高于 list 列表
- tuple 元组相同数据内存占用低于 list列表
- tuple 元组没有增删改方法
- Python的深浅拷贝
https://www.cnblogs.com/wilber2013/p/4645353.html
- 浅拷贝总结:列表
- 浅拷贝会创建新的列表对象;但是对于对象中的元素,浅拷贝会使用原始元素的引用(内存地址)
- 对于对象中的元素改变,如果是不可变对象:会创建新对象;如果是可变对象:会直接修改元素的值
- 浅拷贝总结: 列表
- 浅拷贝会创建新的列表对象,并且对于可变对象也会创建新的对象;对于不可变对象会使用原始元素的引用(内存地址)
- 对于对象中的元素改变,如果是不可变对象:会创建新对象;如果是可变对象:会直接修改元素的值
浅拷贝代码示例:
import copy
a = [1,"2",[3,4]]
b = copy.copy(a)
print(id(a),id(b)) # 内存地址改变说明创建新的对象
print(id(index) for index in a)
print(id(index) for index in b) # 引用对象中的原始元素
b[0] = "a"
print(a,b) # 修改不可变对象,会重新创建新的对象,对源对象没有影响
b[2][0] = "A"
print(a,b) # 修改可变对象,直接修改元素的值,对原对象会有影响
=====结果:
2453878163528 2453879464584
<generator object <genexpr> at 0x0000023B5687EF68>
<generator object <genexpr> at 0x0000023B5687EF68>
[1, '2', [3, 4]] ['a', '2', [3, 4]]
[1, '2', ['A', 4]] ['a', '2', ['A', 4]]
深拷贝代码示例:
import copy
a = [1,"2",[3,4]]
b = copy.deepcopy(a)
print(id(a),id(b)) # 内存地址改变说明创建新的对象
print(id(index) for index in a)
print(id(index) for index in b) # 引用对象中的原始元素;但对于可变对象会创建新的对象
print(id(a[2]),id(b[2])) # 1599702285704 1599702270344
b[0] = "a"
print(a,b) # 修改不可变对象,会重新创建新的对象,对源对象没有影响
b[2][0] = "A" # 修改可变对象,直接修改元素的值,对原对象没有影响
print(a,b)
======结果:
2535615973448 2535617299080
<generator object <genexpr> at 0x0000024E5E7C1F10>
<generator object <genexpr> at 0x0000024E5E7C1F10>
[1, '2', [3, 4]] ['a', '2', [3, 4]]
[1, '2', [3, 4]] ['a', '2', ['A', 4]]
- 赋值操作:列表
- 不会创建新的列表对象,即变量指向相同的对象
- Python迭代器/生成器/装饰器/闭包
- 可迭代的: 能够被for循环都是可迭代的
- 可迭代协议:内部实现"__iter__" 方法,该对象也称之为可迭代对象
- 迭代器:访问集合元素的一种方式;必须拥有“__iter__”和“__next__”方法
特点:
- 访问者不需要关心迭代器内部结构,仅需通过next()方法不断获取下一个内容
- 不能随机访问集合中的某个值,只能从头到尾依次访问
- 访问到一半时不能往回退
- 便于循环比较大的数据集合,节省内存
- 生成器:一个函数调用时返回一个迭代器,那这个函数就叫做生成器,如果函数中包含yield语法,那这个函数就会变成生成器函数
- 生成器函数:用关键字 yield 来返回值,函数被调用时会返回一个生成器对象
- 生成器本质:迭代器
- 生成器取值: 可以使用for循环和next()方法来实现
代码示例:
def cash_out(count):
while count > 0:
count -= 1
print("擦,又来取钱了....败家子! 还剩 %s " % count)
yield 1
ATM = cash_out(5) # 返回一个生成器对象
print(ATM)
print("取到钱 %s 万" % (ATM.__next__())) # 调用next方法;执行函数到yield停止并返回值;send方法和next方法相同并能够给yield传值
print("取到钱 %s 万" % (ATM.__next__()))
- 列表推导式: a = [i+1 for i in range(10)]
- 生成器表达式: a = (i+1 for i in range(10))
- 闭包函数:内部函数包含对外部作用域而非全局作用域名字的引用,该内部函数称为闭包函数
- 闭包:在一个外函数中定义了一个内函数,内函数引用外函数的临时变量,并且外函数的返回值是内函数的引用
代码示例:
# print_msg是外围函数
def print_msg():
msg = "I'm closure"
# printer是嵌套函数
def printer():
print(msg)
return printer
# 这里获得的就是一个闭包
closure = print_msg()
# 输出 I'm closure
closure()
- 装饰器: 在不修改原函数及其调用方式的情况下对原函数功能进行扩展;本质上:一个闭包函数
- 语法糖: 返回内部函数名;然后赋值给原函数名
代码示例:
import time
def timer(func): # timer(test)
def wrapper():
start_time = time.time()
func()
end_time = time.time()
print("This is taken %s" % (end_time - start_time))
return wrapper
@timer # test1 = timer(test1) # 语法糖
def test1():
time.sleep(3)
print("This is in test1 function!")
test1()
- 装饰器固定格式:
import functools import wraps
def decorator(func):
@wraps(func) #加在最内层函数正上方,可以使用关于查看函数信息的方法
def inner(*args,**kwargs):
print("before ex func")
re = func(*args,**kwargs)
print("after ex func")
return re
return inner
- Python的假线程
- GIL本质:一把互斥锁,将并发运行变成串行,以此来控制同一时间内共享数据只能被一个任务所修改,进而保证数据安全。
- 锁的目的:是为了保护共享的数据,同一时间只能有一个线程来修改共享的数据
- GIL 与Lock是两把锁,保护的数据不一样,前者是解释器级别的(当然保护的就是解释器级别的数据,比如垃圾回收的数据),
后者是保护用户自己开发的应用程序的数据,很明显GIL不负责这件事,只能用户自定义加锁处理,即Lock
- GIL的存在:同一时刻同一进程中只有一个线程被执行(Cpython解释器的锅)
- Python垃圾回收机制
- 引用计数法
优点:a) 高效 b) 实时性:一旦没有引用,内存直接释放 c) 对象有确定的生存周期 d) 易于实现
缺点:a) 维护引用计数的次数和引用赋值成正比 b) 无法解决循环引用问题
- 标记-清除
- 阶段1: 标记 -> 标记[活动对象]
- 阶段2: 清除 -> 清除[非活动对象]
- 流程:
- 每当创建一个对象或者其他什么值的时候,Python会将其加入零代链表中
a) 不能访问对象数据;
b) 包含Python自己使用的内部值
- 检测循环引用:
a) 遍历0代链表上每个对象,检测每个互相引用的对象,根据规则减掉其引用数
b) 当引用计数为0意味着可以释放它们并回收内存,并且将活跃对象移动到新的链表(一代链表)
- Python的GC阙值:当被分配对象的计数值与被释放对象的计数值之间的差异累计值到达GC阙值,就启动Python的收集机制
a) 随着时间的推移,程序所使用的对象逐渐从零代列表移动到一代列表
b) Python对于一代链表中对象的处理遵循同样的方法,一旦被分配计数值与被释放计数值累计到达一定阈值,Python会将剩下的活跃对象移动到二代链表。
c) 通过不同的阈值设置,Python可以在不同的时间间隔处理这些对象。Python处理零代最为频繁,其次是一代然后才是二代
- 弱代假说:
- 垃圾回收算法核心: 垃圾回收器会频繁的处理新对象,一个新对象即程序刚刚创建,一个老对象即经过几个周期仍然存在的
- 观点:首先是年轻的对象通常死得也快,而老对象则很有可能存活更长的时间
- 分代回收:
- 是一种以空间换时间的操作方式,Python将内存根据对象的存活时间划分为不同的集合,每个集合称为一个代(一共3代)
- 垃圾收集频率与对象的存活时间的增大而减小
- 新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾收集机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,
依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内
参考博客:https://www.jianshu.com/p/1e375fb40506
源码解释:https://blog.csdn.net/xiongchengluo1129/article/details/80462651
- Python api签名
- Django框架
博客参考:https://www.cnblogs.com/wenyule/p/9740926.html
- Python赋值操作
代码:
a = 1 # 内存会为1创建一个数据单元,再让a指向这个单元
print(id(a)) # 1940417552
a = 2 # 内存会为2重新创建一个数据单元,再让a重新指向这个单元
print(id(a)) # 1940417584
- a变量指向的内存地址发生变化
- Python是直接操控数据,数据处于主动地位,变量只是作为一种引用的关系存在,而不再拥有存储的功能
- “数据在python中称之为对象”,对象不能够被覆盖,也不能被销毁(垃圾回收机制会来回收不用的对象)
- 对象分类:
- 不可变对象:int str float tuple
- 可变对象:dict set list
- 可变不可变指的是:指向是否可变
- 不可变对象:对象存放地址中的值会改变
- 具有相同值的不可变对象是同一个对象
- 只要不可变对象指向的值发生变化,不可变对象就会重新指向新的值
- 可变对象:对象存放地址中的值不会改变(即重新创建地址存放新对象)
- 具有相同值的可变对象是不同的对象
- 允许同一对象中的值发生变化,但是首地址不会发生改变
- Python列表和元组比较
- tuple 元组是一个只读版本的 list 列表
- tuple 元组初始化效率要高于 list 列表
- tuple 元组相同数据内存占用低于 list列表
- tuple 元组没有增删改方法
- Python的深浅拷贝
https://www.cnblogs.com/wilber2013/p/4645353.html
- 浅拷贝总结:列表
- 浅拷贝会创建新的列表对象;但是对于对象中的元素,浅拷贝会使用原始元素的引用(内存地址)
- 对于对象中的元素改变,如果是不可变对象:会创建新对象;如果是可变对象:会直接修改元素的值
- 浅拷贝总结: 列表
- 浅拷贝会创建新的列表对象,并且对于可变对象也会创建新的对象;对于不可变对象会使用原始元素的引用(内存地址)
- 对于对象中的元素改变,如果是不可变对象:会创建新对象;如果是可变对象:会直接修改元素的值
浅拷贝代码示例:
import copy
a = [1,"2",[3,4]]
b = copy.copy(a)
print(id(a),id(b)) # 内存地址改变说明创建新的对象
print(id(index) for index in a)
print(id(index) for index in b) # 引用对象中的原始元素
b[0] = "a"
print(a,b) # 修改不可变对象,会重新创建新的对象,对源对象没有影响
b[2][0] = "A"
print(a,b) # 修改可变对象,直接修改元素的值,对原对象会有影响
=====结果:
2453878163528 2453879464584
<generator object <genexpr> at 0x0000023B5687EF68>
<generator object <genexpr> at 0x0000023B5687EF68>
[1, '2', [3, 4]] ['a', '2', [3, 4]]
[1, '2', ['A', 4]] ['a', '2', ['A', 4]]
深拷贝代码示例:
import copy
a = [1,"2",[3,4]]
b = copy.deepcopy(a)
print(id(a),id(b)) # 内存地址改变说明创建新的对象
print(id(index) for index in a)
print(id(index) for index in b) # 引用对象中的原始元素;但对于可变对象会创建新的对象
print(id(a[2]),id(b[2])) # 1599702285704 1599702270344
b[0] = "a"
print(a,b) # 修改不可变对象,会重新创建新的对象,对源对象没有影响
b[2][0] = "A" # 修改可变对象,直接修改元素的值,对原对象没有影响
print(a,b)
======结果:
2535615973448 2535617299080
<generator object <genexpr> at 0x0000024E5E7C1F10>
<generator object <genexpr> at 0x0000024E5E7C1F10>
[1, '2', [3, 4]] ['a', '2', [3, 4]]
[1, '2', [3, 4]] ['a', '2', ['A', 4]]
- 赋值操作:列表
- 不会创建新的列表对象,即变量指向相同的对象
- Python迭代器/生成器/装饰器/闭包
- 可迭代的: 能够被for循环都是可迭代的
- 可迭代协议:内部实现"__iter__" 方法,该对象也称之为可迭代对象
- 迭代器:访问集合元素的一种方式;必须拥有“__iter__”和“__next__”方法
特点:
- 访问者不需要关心迭代器内部结构,仅需通过next()方法不断获取下一个内容
- 不能随机访问集合中的某个值,只能从头到尾依次访问
- 访问到一半时不能往回退
- 便于循环比较大的数据集合,节省内存
- 生成器:一个函数调用时返回一个迭代器,那这个函数就叫做生成器,如果函数中包含yield语法,那这个函数就会变成生成器函数
- 生成器函数:用关键字 yield 来返回值,函数被调用时会返回一个生成器对象
- 生成器本质:迭代器
- 生成器取值: 可以使用for循环和next()方法来实现
代码示例:
def cash_out(count):
while count > 0:
count -= 1
print("擦,又来取钱了....败家子! 还剩 %s " % count)
yield 1
ATM = cash_out(5) # 返回一个生成器对象
print(ATM)
print("取到钱 %s 万" % (ATM.__next__())) # 调用next方法;执行函数到yield停止并返回值;send方法和next方法相同并能够给yield传值
print("取到钱 %s 万" % (ATM.__next__()))
- 列表推导式: a = [i+1 for i in range(10)]
- 生成器表达式: a = (i+1 for i in range(10))
- 闭包函数:内部函数包含对外部作用域而非全局作用域名字的引用,该内部函数称为闭包函数
- 闭包:在一个外函数中定义了一个内函数,内函数引用外函数的临时变量,并且外函数的返回值是内函数的引用
代码示例:
# print_msg是外围函数
def print_msg():
msg = "I'm closure"
# printer是嵌套函数
def printer():
print(msg)
return printer
# 这里获得的就是一个闭包
closure = print_msg()
# 输出 I'm closure
closure()
- 装饰器: 在不修改原函数及其调用方式的情况下对原函数功能进行扩展;本质上:一个闭包函数
- 语法糖: 返回内部函数名;然后赋值给原函数名
代码示例:
import time
def timer(func): # timer(test)
def wrapper():
start_time = time.time()
func()
end_time = time.time()
print("This is taken %s" % (end_time - start_time))
return wrapper
@timer # test1 = timer(test1) # 语法糖
def test1():
time.sleep(3)
print("This is in test1 function!")
test1()
- 装饰器固定格式:
import functools import wraps
def decorator(func):
@wraps(func) #加在最内层函数正上方,可以使用关于查看函数信息的方法
def inner(*args,**kwargs):
print("before ex func")
re = func(*args,**kwargs)
print("after ex func")
return re
return inner
- Python的假线程
- GIL本质:一把互斥锁,将并发运行变成串行,以此来控制同一时间内共享数据只能被一个任务所修改,进而保证数据安全。
- 锁的目的:是为了保护共享的数据,同一时间只能有一个线程来修改共享的数据
- GIL 与Lock是两把锁,保护的数据不一样,前者是解释器级别的(当然保护的就是解释器级别的数据,比如垃圾回收的数据),
后者是保护用户自己开发的应用程序的数据,很明显GIL不负责这件事,只能用户自定义加锁处理,即Lock
- GIL的存在:同一时刻同一进程中只有一个线程被执行(Cpython解释器的锅)
- Python垃圾回收机制
- 引用计数法
优点:a) 高效 b) 实时性:一旦没有引用,内存直接释放 c) 对象有确定的生存周期 d) 易于实现
缺点:a) 维护引用计数的次数和引用赋值成正比 b) 无法解决循环引用问题
- 标记-清除
- 阶段1: 标记 -> 标记[活动对象]
- 阶段2: 清除 -> 清除[非活动对象]
- 流程:
- 每当创建一个对象或者其他什么值的时候,Python会将其加入零代链表中
a) 不能访问对象数据;
b) 包含Python自己使用的内部值
- 检测循环引用:
a) 遍历0代链表上每个对象,检测每个互相引用的对象,根据规则减掉其引用数
b) 当引用计数为0意味着可以释放它们并回收内存,并且将活跃对象移动到新的链表(一代链表)
- Python的GC阙值:当被分配对象的计数值与被释放对象的计数值之间的差异累计值到达GC阙值,就启动Python的收集机制
a) 随着时间的推移,程序所使用的对象逐渐从零代列表移动到一代列表
b) Python对于一代链表中对象的处理遵循同样的方法,一旦被分配计数值与被释放计数值累计到达一定阈值,Python会将剩下的活跃对象移动到二代链表。
c) 通过不同的阈值设置,Python可以在不同的时间间隔处理这些对象。Python处理零代最为频繁,其次是一代然后才是二代
- 弱代假说:
- 垃圾回收算法核心: 垃圾回收器会频繁的处理新对象,一个新对象即程序刚刚创建,一个老对象即经过几个周期仍然存在的
- 观点:首先是年轻的对象通常死得也快,而老对象则很有可能存活更长的时间
- 分代回收:
- 是一种以空间换时间的操作方式,Python将内存根据对象的存活时间划分为不同的集合,每个集合称为一个代(一共3代)
- 垃圾收集频率与对象的存活时间的增大而减小
- 新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾收集机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,
依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内
参考博客:https://www.jianshu.com/p/1e375fb40506
源码解释:https://blog.csdn.net/xiongchengluo1129/article/details/80462651
- Python api签名
- Django框架
博客参考:https://www.cnblogs.com/wenyule/p/9740926.html