Python进阶(六) -- 高级语法

本文深入探讨了Python中的GIL全局解释锁,解释了其存在原因和弊端,并提出了应对策略。同时,文章详细讨论了Python中的可变与不可变类型,特别是深浅拷贝的差异,以及如何进行正确的拷贝操作。此外,还涵盖了import导入模块、拆包、继承、property装饰器的使用等高级语法特性。
摘要由CSDN通过智能技术生成

GIL全局解释锁

定义

GIL锁:Global Interpreter Lock,又称为全局解释锁。

任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。GIL锁实际上把所有线程的执行代码都给上了锁。所以,多线程在Python中智能交替执行,即使100个线程跑在100核CPU上也只能用到一核。

GIL锁不是Python特性

GIL是Python解释器(Cpython)时引入的概念,在JPython、PyPy中没有GIL.GIL并不是Python的语言缺陷。是解释器层级的锁,跟Python语言特性无关

GIL存在原因

  1. 早期计算机都是单核设计
  2. CPython在执行多线程时并不是线程安全的,所以为了程序的稳定性,加一把全局解释锁,能够确保任何时候都只有宇哥Python线程执行

GIL的弊端

  • 对计算密集型的程序产生影响,因为计算密集型的程序,需要占用系统资源
  • GIL的存在,相当于始终在进行单线程运算
  • IO密集型影响不大的原因在于,IO,input、output,这两个词就表明程序的瓶颈在于输入所耗费的时间,线程大部分时间在等待

GIL锁释放

  • 在当前线程执行超时后会自动释放
  • 在当前线程指向阻塞操作时会自动释放
  • 当前执行完成时

GIL解决方案

由于GIL锁的原因,导致多线程并不能很好地利用CPU资源,所以需要解决GIL锁的问题

  1. 换解释器

  2. 使用多进程替换多线程

  3. 使用Python特性:胶水,让子线程用c语言来写

    • 将c语言写的子线程编译为.so文件
    • 使用ctypes加载.so文件
    • 开启多线程

Python中的可变和不可变

可变与不可变是指内存中的那块内容是否可以被改变

  • 可变类型(mutable),创建后可以继续修改对象的内容,如字典、列表

  • 不可变类型(unmutable),一旦创建不可修改内容,如数字、字符串、元组

验证:

# # 数值
# a = 5
# print('a的值为:', a, '\na的内存地址为:', id(a))
# # 改变后内存会重新赋予一个内存地址
# a = 5 + 1
# print('a的值为:', a, '\na的内存地址为:', id(a))
#
# # 字符串
# b = 'hahazhong'
# print('b的值为:', b, '\nb的内存地址为:', id(b))
# # 改变后内存会重新赋予一个内存地址
# b = 'hahazhong' + '120'
# print('改变后b的值为:', b, '\nb的内存地址为:', id(b))


def test(a, b):
    c = id(a)
    print(f'------{
     type(a)}------------')
    print(f'原来的值为: {
     a} \n内存地址为: {
     id(a)}')
    # 改变后内存会重新赋予一个内存地址
    a += b
    print(f'改变后的值为:{
     a} \n内存地址为: {
     id(a)}')
    if id(a) == c:
        print('内存地址未改变')
    else:
        print('内存地址改变')


# 数值
a = 5
test(a, a)

# 字符串
b = '哈哈钟'
test(b, b)

# 元组
c = (1, 2, 3, 4)
test(c, c)

# 列表
d = [1, 2, 3]
test(d, d)

# 字典
e = {
   '1': 2, '3': 4}
f = {
   '5': 6}
print(f'------{
     type(e)}------------')
print(f'原来的值为: {
     e} \n内存地址为: {
     id(e)}')
e.update(f)
print(f'改变后的值为: {
     e} \n内存地址为: {
     id(e)}')

深浅拷贝

python中的copy模块,可以实现拷贝功能

  • 浅拷贝

    引用(地址)拷贝,并没有产生新的空间。如果拷贝的是对象,原对象和copy对象都指向同一个内存空间,只拷贝父对象,不会拷贝对象的内部的子对象。

    用法:copy.copy(变量)进行拷贝

  • 深拷贝

    会产生新的空间.如果拷贝的是对象,原对象和copy都指向不同的内存空间,会拷贝对象及其子对象(产生新的空间)

    用法:copy.deepcopy(变量名)

作用:

  1. 减少内存的使用
  2. 以后在做数据的清洗、修改或者入库时,对源数据进行复制一份,以防数据修改后找不到源数据

简单可变类型深浅拷贝

# @object   : 可变类型深浅拷贝都会产生新的空间,通过对列表进想赋值和深浅拷贝熟悉深浅拷贝
import copy

list1 = [1, 2, 3]
print('list1的内存地址为:', id(list1))

print('--' * 10)
# 赋值
# 赋值后list4的值修改list1也会改变,无法保证数据独立性
list4 = list1
print('list4的内存地址为:', id(list4))
print('--' * 10)
list4.append(77)
print(list1, 'list1的内存地址为:', id(list1))
print(list4, 'list4的内存地址为:', id(list4))
print('--' * 10)
# 浅拷贝

list2 = copy.copy(list1)
print('list2的内存地址为:', id(list2))
list2.append(88)
print(list1, 'list1的内存地址为:', id(list1))
print('list2的内存地址为:', id(list2))
# 结论:可变类型浅拷贝也会产生新的空间地址,保证数据独立性
print('--' * 10)
# 深拷贝
# 可变类型深拷贝会产生新的空间地址,保证数据独立性
list3 = copy.deepcopy(list1)
print('list3的内存地址为:', id(list3))
list3.append(99)
print(list1, 'list1的内存地址为:', id(list1))
print('list3的内存地址为:', id(list3))

总结:

  1. 浅拷贝:

    • 不会产生新的空间
    • 源对象和副本对象指向同一个空间
    • 如果拷贝的对象,子对象不会拷贝
  2. 深拷贝:

    • 产生新的空间
    • 能够保持各自独立性
    • 如果拷贝的是对象,子对象不会拷贝

    简单可变类型不管深拷贝还是浅拷贝都会产生新的空间,保持各自独立性

复杂可变类型的深浅拷贝

# @date     : 2022/11/2 16:40
# @author   : hahazhong
# @coding   : UTF-8
# @object   : 熟悉复杂类型的深浅拷贝
import copy

a = [1, 2]
b = [22, 33, 44]
list = [a, b]
print('a=', a, id(a))
print('b=', b, id(b))
print('list=', list, id(list))
print('list[0]=', list[0], id(list[0]))
print('list[1]=', list[1], id(list[1<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哈哈钟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值