前言
在Python编程的进阶道路上,理解变量作用域、垃圾回收、拷贝机制与异常处理至关重要。本文将深入探讨这些核心概念,助你编写更健壮、高效的代码。从变量作用域到内存管理,再到数据拷贝与异常捕获,让我们一同揭开Python编程的深层奥秘。
一、变量的作用域
1.命名空间
内置命名空间 — python解释器范围下
全局命名空间 — py文件下
局部命名空间 — 函数或类下
2.作用域
内置作用域 校长 全局作用域 年级主任 嵌套作用域 班主任 局部作用域 讲师 # 全局作用域下的变量:全局变量 a = 10 def func(): # 嵌套作用域下的变量 c = 30 def func1(): # 局部作用域下的变量:局部变量 b = 20 def、class、lambda 是可以引入新作用域的
3.LEGB法则
LEGB:作用域的查询顺序(就近原则) 内置作用域 built-in B 全局作用域 Global G 嵌套作用域 Enclosed E 局部作用域 Local L 局部-->嵌套-->全局-->内置 # 全局作用域下的变量:全局变量 a = 10 def func(): # 嵌套作用域下的变量 a = 30 def func1(): # 局部作用域下的变量:局部变量 a = 20 print(a) func1() func()
4.修改全局变量
a = 10 def func(): # 修改全局变量的值--》不可变数据类型 # global 要修改的全局变量的变量名 # 换行对变量进行重新赋值 global a a = 20 print(a) func() print(a)
5.修改嵌套作用域下的变量
def outer(): a = 10 def inner(): # 修改嵌套作用域下的变量 nonlocal # nonlocal 要修改的嵌套作用域下的变量名 # 换行给变量重新赋值 nonlocal a a += 1 print(a) inner() print(a) outer()
二、python垃圾回收机制
1. 引用计数
每一个对象会维护一个ob_ref表,表中存放当前对象的被引用次数; 当对象的被引用次数为0,当前对象会被作为垃圾进行收回 import sys # 引用计数 # 获取对象的被引用次数 print(sys.getrefcount(1)) # 引用计数+1 a = 1 b = a lst = [1,2,3,4] # 引用计数-1 b = 2 del a print(sys.getrefcount(1))
2. 标记清除
跟其名称一样,该算法在进行垃圾回收时分成了两步,分别是: - A)标记阶段,遍历所有的对象,如果是可达的(reachable),也就是还有对象引用它,那么就标记该对象为可达; - B)清除阶段,再次遍历对象,如果发现某个对象没有标记为可达,则就将其回收。 缺陷:在执行标记清除过程中,会将其他正在执行的程序进行终止。会使得资源利用率极低
3.分代回收
它将内存中的对象按生存期划分为几个不同的"代",每次只对某一代进行回收, 优点:减少垃圾回收的次数,提高垃圾回收的效率。 它将内存中的对象分为三种:新生代、老年代和永久代。 新生代只包括新创建的对象; 老年代是存放比较“老”的对象,也就是存活比较久的对象; 永久代则存放三种物件:模块、类和常量。
三、深浅拷贝
1. is 和 ==
# is和 == # a == b:判断a和b的值是否相等;如果相等则返回True,否则返回False # a is b:判断a和b是否是同一个数据; # 获取对象的内容地址 id(数据) # 指向关系:整型、浮点型、字符串、元组 (不可变) a = {"name":"张三"} b = {"name":"张三"} print(id(a)) print(id(b)) print(a == b) print(a is b)
2.浅拷贝
import copy # 拷贝模块 copy # 浅拷贝 需要使用copy模块中copy方法 # 结论:浅拷贝只拷贝第一层,深层次的数据改变都会影响其他. # 拷贝不可变数据类型的数据--》不可变数据类型永远指向关系 a = (1,2,3) b = copy.copy(a) print(id(a),id(b)) print(a is b) # 拷贝可变数据类型的数据 list1 = [1,2,3,[4,5,6]] list2 = copy.copy(list1) # 获取整个数据的id,浅拷贝后的数据id不同 print(id(list1),id(list2)) print(list1 is list2) # 查询拷贝前后深层次数据是否是同一个 print(id(list1[3]),id(list2[3])) print(list1[3] is list2[3]) # 修改原数据中浅层数据,拷贝后的数据不会发生变化 list1.append(7) print(list1) print(list2) # 修改原数据中深层数据,拷贝后的数据会随之发生变化 list1[3].append(8) print(list1) print(list2)
3.深拷贝
import copy # 深拷贝 需要使用copy模块deepcopy # 结论:深拷贝是完全拷贝,数据变化只影响自己本身 # 拷贝不可变数据类型--》不可变数据类型永远都是指向关系 a = ("hello",) b = copy.deepcopy(a) print(id(a),id(b)) print(a is b) # 拷贝可变数据类型 list1 = [1,2,3,[4,5,6]] list2 = copy.deepcopy(list1) # 获取整个数据的id,拷贝后的数据id不同 print(id(list1),id(list2)) print(list1 is list2) # 查询拷贝前后深层次数据是同一个 print(id(list1[3]),id(list2[3])) print(list1[3] is list2[3]) # 对原数据的浅层数据进行操作,拷贝后的数据不会发生变化 list1.append(7) print(list1) print(list2) # 对原数据的深层数据进行操作,拷贝后数据不会发生变化 list1[3].append(8) print(list1) print(list2)
4.深浅拷贝的区别
浅拷贝:只拷贝第一层数据,深层数据还是指向关系
深拷贝:完全拷贝,拷贝前后的数据没有关系
四、异常处理
1.简单异常处理
# 简单异常处理 # try: # 存放可能会出现问题的代码 # except 异常类型: # 如果真的出现这个异常,则执行except中的代码 list1 = [1,2,3] try: print(list1[20]) except IndexError: print("好好数数你的索引!!!!")
2.处理多种异常
2.1 第一种方式
# try: # 存放可能会出现问题的代码 # except 异常类型: # 如果真的出现这个异常,则执行except中的代码 # except 异常类型: # 如果真的出现这个异常,则执行except中的代码 list1 = [1,2,3] try: print(list2[20]) except IndexError: print("好好数数你的索引!!!!") except NameError: print("瞧瞧你的变量名~")
2.2 第二种方法
# try: # 存放可能会出现问题的代码 # except (异常类型1,异常类型2): # 如果真的出现这个异常,则执行except中的代码 list1 = [1,2,3] try: print(list1[20]) except (IndexError,NameError): print("好好看看你的变量")
2.3 第三种方式
# try: # 存放可能会出现问题的代码 # except: # 如果真的出现异常,则执行except中的代码 list1 = [1,2,3] try: list1.add(4) print(list2[20]) except: print("好好看看你的变量")
3.获取异常信息
3.1 单个异常
# 获取错误信息 # 异常类型 as 变量:将出现当前异常的错误信息赋值给变量 list1 = [1,2,3] try: print(list1[20]) except IndexError as e: print(e)
3.2 多个异常
# 获取错误信息 # 异常类型 as 变量:将出现当前异常的错误信息赋值给变量 list1 = [1,2,3] try: print(list1[20]) except (IndexError,NameError) as e: print(e)
4.万能异常处理
# Exception:所有常规异常的基类,包含所有的常规异常 # try: # 存放可能会出现问题的代码 # except Exception as e: # print(e) # 如果真的出现异常,则执行except中的代码;e接收所有的错误信息 list1 = [1,2,3] try: list1.add(4) print(list1[20]) except Exception as e: print(e)
5.else
# try: # 可能会出现问题的代码 # except Exception as e: # 如果真的出现异常则执行except中的代码块 # else: # 如果代码没有出现异常则执行else中的代码块 list1 = [1,2,3] try: print(list1[20]) except Exception as e: print(e) else: print("嘿嘿嘿嘿嘿嘿")
6.finally
# finally-->可以只与try搭配使用 # try: # 可能会出现问题的代码 # except Exception as e: # 如果代码出现问题则执行except中的代码块 # else: # 如果代码没有问题则执行else中的代码块 # finally: # 无论代码是否出现问题最终都会执行finally中的代码块 list1 = [1,2,3] try: print(list1[2]) except Exception as e: print(e) print("这是except中的代码") else: print("这是else中的代码") finally: print("这是finally中的代码")
7.自定义异常
class MyCls: def __init__(self,s): self.s = s def a(self,a,b): try: return a/b except Exception as e: if self.s == True: print(e) else: # 将原本需要抛出的异常进行抛出 # raise 异常类型 -->在程序中raise 后面的异常类型可以不指定 raise my = MyCls(True) print(my.a(10,0))