Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the wp-pagenavi domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /var/www/blog.zhujinhui.net/wp-includes/functions.php on line 6114

Notice: 函数 _load_textdomain_just_in_time 的调用方法不正确twentyseventeen 域的翻译加载触发过早。这通常表示插件或主题中的某些代码运行过早。翻译应在 init 操作或之后加载。 请查阅调试 WordPress来获取更多信息。 (这个消息是在 6.7.0 版本添加的。) in /var/www/blog.zhujinhui.net/wp-includes/functions.php on line 6114
python – 第 8 页 – 煅魂-JeffreyChu的修炼屋

Python面试官问:浅拷贝与深拷贝有何区别与应用?

在 Python 中,浅拷贝和深拷贝是两种复制对象的方法,它们在处理复杂数据结构(如列表、字典、对象等)时的行为不同。让我们来详细了解这两者的区别以及它们的应用场景。

概念与区别

浅拷贝(Shallow Copy)

概念:浅拷贝创建一个新对象,但不递归复制原对象内的子对象。相反,新对象中包含的子对象是对原对象中子对象的引用。因此,原对象和新对象共享相同的子对象。
使用:在 Python 中,使用 copy 模块中的 copy 函数进行浅拷贝:

import copy
original_list = [[1, 2, 3], [4, 5, 6]]
shallow_copy = copy.copy(original_list)

行为:浅拷贝后的对象是独立的对象,但子对象仍然是原对象中的相同引用。这意味着对新对象中的子对象的修改会影响原对象中的相同子对象。
应用场景:在你希望复制对象,但仍然保持对子对象的引用的情况下,可以使用浅拷贝。这在一些需要共享子对象的情况下可能是有用的。

深拷贝(Deep Copy)

概念:深拷贝创建一个新对象,并递归复制原对象中的所有子对象。因此,新对象与原对象完全独立,任何一方的修改都不会影响另一方。
使用:在 Python 中,使用 copy 模块中的 deepcopy 函数进行深拷贝:

import copy
original_list = [[1, 2, 3], [4, 5, 6]]
deep_copy = copy.deepcopy(original_list)

行为:深拷贝后的对象与原对象完全独立。你可以在新对象中自由修改子对象,而不会影响原对象中的子对象。
应用场景:在你希望创建一个完全独立的对象,而不共享任何子对象的引用的情况下,可以使用深拷贝。这在你希望确保对原对象的独立性和完整性时非常有用。
总结
区别:浅拷贝只复制对象本身,而不复制其内部的子对象。深拷贝递归复制对象及其内部的所有子对象。
应用场景:根据你的需求选择使用浅拷贝或深拷贝。如果你希望对象之间共享子对象的引用,可以使用浅拷贝;如果你希望对象完全独立,可以使用深拷贝。
例如:

import copy

# 示例数据
original_list = [[1, 2, 3], [4, 5, 6]]

# 浅拷贝
shallow_copy = copy.copy(original_list)

# 深拷贝
deep_copy = copy.deepcopy(original_list)

# 修改原列表中的子列表
original_list[0].append(99)

print("原列表:", original_list)
print("浅拷贝:", shallow_copy)  # 浅拷贝中的子列表也受影响
print("深拷贝:", deep_copy)  # 深拷贝中的子列表不受影响

在这个例子中,你可以看到对原列表中的子列表的修改会影响浅拷贝,但不会影响深拷贝。

应用场景

浅拷贝

浅拷贝在实际应用中非常有用,特别是在需要复制对象,但不需要完全独立的对象副本时。下面是一个用常见的学校班级换了数学老师的例子来展示浅拷贝,展示如何在管理对象引用、共享数据结构以及避免不必要的深拷贝时使用浅拷贝。

import copy

math_teacher = {"name": "AAA"}
# 学生模板
student_a = {
    "name": "student_a",
    "math_teacher": math_teacher
}

student_b = copy.copy(student_a)
# 浅拷贝出来的对象修改name属性是不会影响class1_student原对象的
student_b["name"] = "student_b"

print("换老师前", student_a, student_b)
# 学生b知道学校更换老师,学生a也会知道
student_b["math_teacher"]["name"] = "BBB"
print("换老师后", student_a, student_b)

# Outputs
# 换老师前 {'name': 'student_a', 'math_teacher': {'name': 'AAA'}} {'name': 'student_b', 'math_teacher': {'name': 'AAA'}}
# 换老师后 {'name': 'student_a', 'math_teacher': {'name': 'BBB'}} {'name': 'student_b', 'math_teacher': {'name': 'BBB'}}

这里可以看到浅拷贝出来的新对象(student_b),将子对象(math_teacher)老师的名字(name)改成“BBB”后,原对象(student_a)的老师名字也从“AAA”变成了“BBB”,说明了对新对象中的子对象的修改会影响原对象中的相同子对象。这里的不用深拷贝的原因,他们的老师是共享对象,改一个地方就可以了,否则每个深拷贝出来的对象都需要重新把子对象老师的名字给重新赋值一遍,增加操作复杂度。

深拷贝在 Python 中也有许多实际应用场景,特别是在需要复制复杂数据结构而不希望子对象之间共享引用的情况下。深拷贝通过递归复制对象及其子对象,确保原对象和副本之间完全独立。

比如某些全局配置是独一份的且不能随意修改的,如果浅拷贝后然后要将数据结构传递给不同的函数或模块使用,可能会产生一些意想不到副作用(可能会被函数/模块修改了影响全局);又或者是在一些数据结构需要备份的时候也会考虑深拷贝。

总结

浅拷贝和深拷贝是python面试中最常问到的问题,也是可变对象和不可变对象概念的延伸。

  • 对于不可变对象(如Python中的int、float、str、tuple等),深拷贝和浅拷贝的结果是相同的,因为这些对象本质上是不可改变的,并且在浅拷贝时并不会引用其他对象。
  • 对于可变对象(如Python中的list、dict、set等),浅拷贝和深拷贝的行为有很大不同。浅拷贝会导致共享引用,而深拷贝会创建完全独立的副本。

pycharm的debug功能原来这么好用

大部分程序猿都知道,不管是开发新功能还是修复BUG,其实真正写代码的时间是非常少,因为大部分的时间都是消耗在了运行/测试也就是调试(debug)上了,所以掌握快捷高效的调试方法将会让你事半功倍。

PyCharm是由JetBrains公司开发的Python集成开发环境(IDE),凭借其强大的调试功能而广受欢迎。今天将详细介绍PyCharm的调试功能,非常简单又好用。

PyCharm调试功能简介

PyCharm提供了多种强大的调试工具,它让我们能够清楚了解代码每一步是怎么运行的,尤其是对新手或者对熟悉新项目的非常友好和有效。调试(debug)功能其中包含自定义命令断点设置调试控制变量监控表达式评估日志点条件断点

接下来以使用python http库请求百度首页的一个简单脚本来展示调试(debug)功能使用过程。

自定义命令

打开调试(debug)配置,点击“+”创建一个新的调试(debug),或者对一个已经存在的配置进行修改

这个面板支持以下重要的配置修改:

  • python环境版本
  • 脚本文件路径,每个调试都可以指定不同的脚本
  • 脚本参数
  • 脚本所运行目录
  • 运行脚本时的环境变量,这在本地调试非常方便,可以用于区别线上生产环境

修改完之后,点击应用(apply)和保存(save)来应用这些配置,当你点击调试(debug)时这些面板参数组成一个shell命令进行执行,类似于

python xxxx.py --abc -def 123

设置断点

首先,打开需要调试的Python文件。在代码编辑器中,单击行号旁边的空白区域以设置断点。断点将以红点的形式显示。

启动调试

由于刚刚上面已经面板参数中已经指定了具体的脚本了,所以点击调试(debug)按钮(一个绿色长得像虫子的按钮)就可以开始执行脚本调试了

还一种更快捷的方式启动调试,就是右键单击文件中的代码,然后选择调试文件(Debug ‘filename’)(或通过快捷键Shift + F9)。PyCharm将启动调试会话

使用调试控制选项

一旦程序暂停在断点处,此时PyCharm提供了以下调试控制选项(mac的键位可能有点不一样,以下是window的键位):

继续执行(F9):继续执行代码,直到遇到下一个断点或程序结束,在循环比较多的代码非常好用。
逐步执行(F8):逐行执行代码,并自动进入函数调用,这个在一些复杂的代码里非常好用。
逐步出入函数(Shift + F8):逐步进入或退出函数调用。
逐步进入(F7):逐行执行代码,并进入函数调用。

查看变量和表达式求值

在调试过程中,你可以查看“变量”窗口中的变量值。你还可以在”监视(Watches)“选项卡中添加自定义监视表达式。例子中我们可以清楚看到脚本里每个中间变量如“url”以及一些预测变量如”resp.read().decode(“utf-8”)“。


另外,你可以通过右键单击代码并选择”对表达式求值(Evaluate Expression)“来评估表达式。输入表达式并查看结果。

这个功能可以代替平常临时用print去打印一些脚本中间变量的用法,而且也不会产生忘记删除print留下无用日志之类的后遗症,让自己能更加清晰的看到一些中间变量的数据结构逻辑处理过程。

设置日志点和条件断点

在调试过程中,你可以通过右键单击断点并选择”编辑(Edit breakpoint)“来设置日志点和条件断点。输入条件表达式或日志消息,然后保存。比如下面这个设置了只有请求为200时才会触发断点,其他情况则会跳过不触发。

四、总结
PyCharm提供了全面而强大的调试工具,使得调试Python代码变得更加容易和高效。通过熟练掌握这些功能,你可以更快地找到代码中的问题并进行修复,提高开发和调试效率。你学会了吗?