分类
分类
category:
- Python编程
- 进阶知识
标签
tag:
- Python
- 进阶知识
- 深拷贝浅拷贝
- 可变类型
- 高级特性
优先级
order: 40
图标
icon: python
封面
cover: /pic/IT_page13_image_105.jpg
Python进阶知识点:避坑指南与高级技巧
在我五年的Python开发经验中,遇到过无数因为对语言特性理解不深而导致的bug。这些看似简单的知识点,往往是区分初级和高级开发者的关键所在。
今天,我将分享一些在实际开发中容易踩坑的进阶知识点,以及一些虽然高级但在特定场景下非常有用的技巧。掌握这些内容,能让你在Python的道路上走得更稳更远。
深拷贝浅拷贝
import copy
# 浅拷贝
a = [1, 2, 3, 4]
b = a.copy()
print(id(a), id(b))
# 深拷贝
a = [1, 2, 3, 4]
b = copy.deepcopy(a)
print(id(a), id(b))浅拷贝是对于一个对象的顶层拷贝,对于对象中的子对象不会拷贝,而是引用。 深拷贝是对于一个对象所有层次的拷贝(递归)。
这里再引申出一个问题,python的可变类型和不可变类型
什么是可变类型呢,比如说列表,我们可以直接就改变其中的值,list1[1]= 0 比如 list dict 而不可变呢,就是值如果发生改变,就相当于一个新的对象了,比如 int float string tuple 我们可以使用id(xxx)来判断对象的地址
由可变类型和不可变类型,我们在引申出一个问题,就是python的参数传递是值传递还是引用传递 我们可以通过id(xxx)来判断对象的地址,如果地址相同,那么就是值传递,如果地址不同,那么就是引用传递 我们可以通过以下代码来测试
def modify_integer(x):
x = x + 1
a = 5
modify_integer(a)
print(a) # 输出: 5在这个例子中,尽管a被传递给了modify_integer函数,函数内部对x的修改(尽管改变了x的值)并没有影响原始的a。
def modify_list(x):
x.append(2)
a = [1]
modify_list(a)
print(a) # 输出: [1, 2]在这个例子中,a被传递给了modify_list函数,函数内部对列表x的修改(添加了一个元素)影响了原始的a。 所以我们可以得出结论,python的参数传递是值传递还是引用传递,取决于参数的类型,不可变类型是值传递,可变类型是引用传递
深拷贝和浅拷贝这里,我们在引出一个问题 a = b ,对b进行修改,那么a的值会发生变化吗? 答案分两种情况
- 如果a和b是可变类型,那么a的值会发生变化
- 如果a和b是不可变类型,那么a的值不会发生变化
a = [1, 2, 3, 4]
b = a
b[1] = 0
print(a) # 输出: [1, 0, 3, 4]这里的a和b是两个不同的对象,但是他们的值是一样的,所以a的值会发生变化
is 和 ==
- is 是比较两个引用是否指向了同一个对象(引用比较)。
- == 是比较两个对象是否相等。 可以说is是比==更严格的比较 举例:
a = [1, 2, 3, 4]
b = a.copy()
print(a == b) # True
print(a is b) # False这里的a和b是两个不同的对象,但是他们的值是一样的,所以a == b 返回True,但是a is b 返回False 因为a和b是两个不同的对象,所以a is b 返回False
python传参数是值传递还是引用传递
在Python中,参数的传递方式取决于参数的类型。Python中的变量可以是不可变类型(如整数、浮点数、字符串、元组等)或可变类型(如列表、字典、集合等)。
不可变类型 对于不可变类型(如整数、浮点数、字符串、元组等),参数是通过值传递的。这意味着当你将一个不可变类型的变量作为参数传递给函数时,函数内部对这个参数的修改不会影响原始变量。
示例:
def modify_integer(x):
x = x + 1
a = 5
modify_integer(a)
print(a) # 输出: 5在这个例子中,尽管a被传递给了modify_integer函数,函数内部对x的修改(尽管改变了x的值)并没有影响原始的a。
可变类型 对于可变类型(如列表、字典、集合等),参数是通过引用传递的。这意味着当你将一个可变类型的变量作为参数传递给函数时,函数内部对这个参数的修改会影响原始变量。
示例:
def modify_list(x):
x.append(2)
a = [1]
modify_list(a)
print(a) # 输出: [1, 2]在这个例子中,a被传递给了modify_list函数,函数内部对列表x的修改(添加了一个元素)影响了原始的a。
总结 不可变类型是通过值传递的。
可变类型是通过引用传递的。
理解这一点对于编写和理解Python代码非常重要,特别是在处理大型数据结构或需要确保函数不修改原始数据时。如果你希望在函数内部修改数据而不影响原始数据,可以考虑返回一个新的数据结构而不是直接修改传入的参数。例如,对于列表,你可以通过返回一个新的列表来实现这一点:
def modify_list(x):
return x + [2] # 返回新的列表,而不是修改原列表
a = [1]
a = modify_list(a)
print(a) # 输出: [1, 2]这样,原始的a就不会被改变了。
元类
python的重载
工作中就没有使用过,而且python也不需要,下文仅做一个解释 函数重载主要是为了解决两个问题。 1。可变参数类型。 2。可变参数个数。 另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同 的,此时才使用函数重载,如果两个函数的功能其实不同,那么不应当使用重载,而应当使用一个名字 不同的函数。 好吧,那么对于情况 1 ,函数功能相同,但是参数类型不同,python 如何处理?答案是根本不需要处 理,因为 python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python 中 很可能是相同的代码,没有必要做成两个不同函数。 那么对于情况 2 ,函数功能相同,但参数个数不同,python 如何处理?大家知道,答案就是缺省参 数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数 终归是需要用的。 好了,鉴于情况 1 跟 情况 2 都有了解决方案,python 自然就不需要函数重载了。
python的三元运算符
python的GIL
GIL是python的全局解释器锁,同一进程中假如有多个线程运行,一个线程在运行 python程序的时候会霸占pythonf解释器(加了一把锁即GIL),使该进程内的其他线程 无法运行,等该线程运行完后其他线程才能运行。如果线程运行过程中遇到耗时操作,则 解释器锁解开,使其他线程运行。所以在多线程中,线程的运行仍是有先后顺序的,并不 是同时进行。 多进程中因为每个进程都能被系统分配资源,相当于每个进程有了一个oython解释器, 所以多进程可以实现多个进程的同时运行,缺点是进程系统资源开销大
flask Django 和fastapi都有orm模型,那什么是orm模型
orm 是 object relation map的缩写,以为对象映射 实现了数据模型与数据库的解耦,通过简单的配置就可以轻松更换数据库,而不需要修改代码只需要面向对象编程,or操作本质上会根据对接的数据库引擎,翻译成对应的sq语句,所有使用Django开发的项目无需关心程序底层使用的是MySQL、Oracle、sqlite.…,如果数据库迁移,只需要更换Djangol的数据库引擎即可
提高python运行效率的方法
- 使用生成器,因为生成器是惰性的,不会一次性加载所有数据,节约内存
- 使用多进程,因为多进程是并行的,不会因为一个进程阻塞而影响其他进程
- 使用多线程,因为多线程是并发的,不会因为一个线程阻塞而影响其他线程
- 使用协程,因为协程是并发的,不会因为一个协程阻塞而影响其他协程
- 多个if elif条件判断,可以使用字典来存储,这样可以减少if elif的数量,提高效率
python的魔法方法
init 是python的构造函数,当我们创建一个对象的时候,会自动调用这个方法 new 是python的静态方法,当我们创建一个对象的时候,会自动调用这个方法 call 是python的魔法方法,当我们调用一个对象的时候,会自动调用这个方法 str 是python的魔法方法,当我们打印一个对象的时候,会自动调用这个方法 repr 是python的魔法方法,当我们打印一个对象的时候,会自动调用这个方法 len 是python的魔法方法,当我们调用len()函数的时候,会自动调用这个方法
sort和sorted的区别
sort是列表的方法,会改变现有列表的顺序 sorted是内置函数,不会改变列表的顺序 举例:
a = [1, 2, 3, 4]
a.sort()
print(a) # [1, 2, 3, 4]
b = [1, 2, 3, 4]
b = sorted(b)
print(b) # [1, 2, 3, 4]python的引用计数(python的垃圾回收机)
python:垃圾回收主要以引用计数为主,标记-清除和分代清除为辅的机制,其中标记-清除和分代回收主要是为了处理循环引用的难题。
引用计数算法
当有1个变量保存了对象的引用时,此对象的引用计数就会加1当使用d删除变量指向的对象时,如果对象的引用计数不为1,比如3,那么此时只会让这个引用计数减1,即变为2,当再次调用del时,变为1,如果再调用1次de,此时会真的把对象进行删除
python新式类和经典类的区别?
a. 在python里凡是继承了object的类,都是新式类 b. Python3里只有新式类 c. Python2里面继承object的是新式类,没有写父类的是经典类 d. 经典类目前在Python里基本没有应用 e. 保持class与type的统一对新式类的实例执行a.class与type(a)的结果是一致的,对于旧式类来说就不 一样了。 f. 对于多重继承的属性搜索顺序不一样新式类是采用广度优先搜索,旧式类采用深度优先搜索。
我们编写函数的4个原则
其实我简单来说就是: 简单通俗易懂原子化 向下兼容 可扩展 (可扩展可以通过继承来实现)
元类
工作过程中完全没用过,下文仅做一个解释 元类是创建类的类,就像类是创建对象的类一样。在Python中,类是由type类创建的。因此,type是Python的内置元类。
元类的主要用途是创建API,一个典型的例子是Django ORM。Django ORM中,你定义的每个模型类实际上都是通过元类转换成数据库中的表。
元类的主要作用是控制类的创建行为。你可以使用元类来修改类,添加方法,甚至改变类的行为。例如,你可以使用元类来自动为每个类添加一个方法,或者自动为每个类添加一个属性。
