问题标签 [python-internals]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
python - Python 字节码在 CPython 中究竟是如何运行的?
我试图了解 Python 是如何工作的(因为我一直都在使用它!)。据我了解,当你运行 python script.py 之类的东西时,脚本会被转换为字节码,然后解释器/VM/CPython——实际上只是一个 C 程序——读取 python 字节码并相应地执行程序。
这个字节码是如何读入的?它类似于在 C 中读取文本文件的方式吗?我不确定如何将 Python 代码转换为机器代码。Python解释器(CLI中的python命令)是否真的只是一个已经转换为机器代码的预编译C程序,然后python字节码文件只是通过该程序?换句话说,我的 Python 程序是否从未真正转换为机器码?python 解释器是否已经在机器代码中,所以我的脚本永远不必是?
python - 列表理解过滤 - “set() 陷阱”
一个相当常见的操作是list
基于另一个过滤一个list
。人们很快发现:
对于大输入来说很慢 - 它是 O(n*m)。呸。我们如何加快速度?使用 aset
进行过滤查找 O(1):
这给出了很好的整体 O(n) 行为。然而,我经常看到即使是经验丰富的编码人员也陷入了陷阱™:
确认!这又是 O(n*m),因为 pythonset(list_2)
每次都构建,而不仅仅是一次。
我认为这就是故事的结局——python 无法将其优化为只构建set
一次。请注意陷阱。必须忍受它。唔。
呵呵,python(3.3)可以优化掉一组文字。它甚至比f()
这种情况下更快,大概是因为它可以将 a 替换为LOAD_GLOBAL
a LOAD_FAST
。
Python 2 显然没有进行这种优化。我已经尝试进一步调查 python3 正在做什么,但不幸dis.dis
的是无法探究理解表达式的内部结构。基本上所有有趣的东西都会变成MAKE_FUNCTION
.
所以现在我想知道 - 为什么 python 3.x 可以优化设置文字只构建一次,但不能set(list_2)
?
python - 为什么 CPython 字典不受负一和负二的哈希值的影响
哈希表应该是高性能的映射,并且因为 Python dicts 是用哈希表实现的,所以它们也是高性能的。但是在查看负整数的哈希值时,我遇到了一个奇怪的结果。
但这显然对dicts没有影响:
为什么会发生这种情况,为什么我使用它们时不会影响 dicts?
python - 如何在 Python 中解析对变量的引用
这条消息有点长,有很多例子,但我希望它能帮助我和其他人更好地掌握 Python 2.7 中变量和属性查找的完整故事。
对于代码块(例如模块、类定义、函数定义等)和变量绑定(例如作为赋值、参数声明、类和函数声明、for循环等)
我将术语变量用于可以不带点调用的名称,以及需要用对象名称限定的名称的属性(例如对象 obj 的属性 x 的 obj.x)。
Python 中的所有代码块都有三个作用域,但函数:
- 当地的
- 全球的
- 内置
Python 中有四个块仅用于函数(根据 PEP 227):
- 当地的
- 封闭函数
- 全球的
- 内置
将变量绑定到并在块中找到它的规则非常简单:
- 任何将变量绑定到块中的对象都会使该变量成为该块的局部变量,除非该变量被声明为全局变量(在这种情况下,该变量属于全局范围)
- 使用规则 LGB(本地、全局、内置)为所有块查找对变量的引用,但函数
- 仅对函数使用规则 LEGB(本地、封闭、全局、内置)来查找对变量的引用。
让我知道以验证此规则的示例,并展示许多特殊情况。对于每个例子,我都会给出我的理解。如果我错了,请纠正我。对于最后一个例子,我不明白结果。
示例 1:
类没有嵌套范围(规则 LGB),并且类中的函数在不使用限定名称(本例中为 self.x)的情况下无法访问类的属性。这在 PEP227 中有很好的描述。
示例 2:
这里使用 LEGB 规则查找函数中的变量,但如果路径中存在类,则跳过类参数。这也是 PEP 227 所解释的。
示例 3:
我们期望使用诸如 python 之类的动态语言来动态解决所有问题。但对于函数来说,情况并非如此。局部变量在编译时确定。PEP 227 和 http://docs.python.org/2.7/reference/executionmodel.html以这种方式描述了这种行为
“如果名称绑定操作发生在代码块内的任何位置,则块内名称的所有使用都将被视为对当前块的引用。”
示例 4:
但是我们在这里看到 PEP227 中的这条语句“如果名称绑定操作发生在代码块中的任何位置,则块中名称的所有使用都被视为对当前块的引用。” 当代码块是一个类时是错误的。此外,对于类,似乎本地名称绑定不是在编译时进行的,而是在执行期间使用类命名空间进行的。在这方面,PEP227 和 Python 文档中的执行模型具有误导性,并且在某些部分是错误的。
示例 5:
我对这段代码的理解如下。指令 x = x 首先查找表达式右手 x 所指的对象。在这种情况下,对象在类中本地查找,然后按照规则 LGB 在全局范围内查找,即字符串“模块中的 x”。然后在类字典中创建 MyClass 的局部属性 x 并指向字符串对象。
示例 6:
现在这是一个我无法解释的例子。它非常接近示例 5,我只是将本地 MyClass 属性从 x 更改为 y。
为什么在这种情况下 MyClass 中的 x 引用在最里面的函数中查找?
python - 为什么 Python 的 'len' 函数比 __len__ 方法快?
在 Python 中,len
是一个通过调用对象的__len__
方法来获取集合长度的函数:
因此,我希望直接调用 of__len__()
至少与len()
.
但是使用上述代码进行测试的结果显示len()
速度更快。为什么?
python - 切片`a`(例如`a[1:] == a[:-1]`)会创建`a`的副本吗?
我的一个朋友向我展示了以下 Python 代码:
a
如果其中的所有项目都相同,则返回 True 。
我认为代码乍一看很难理解,而且 - 它在内存使用方面效率低下,因为a
将创建两个副本用于比较。
我使用 Pythondis
来查看幕后发生的事情a[1:]==a[:-1]
:
它归结为两个切片命令 -SLICE+1
和SLICE+2
. 文档不清楚这些操作码是否实际上创建了 的新副本a
,或者只是对它的引用。
- SLICE 命令是否复制
a
? - 答案是否因 Python 实现(Cython、Jython)而异?
更新
这个片段显然是不可读和令人困惑的,我不会在实际代码中使用它。我的兴趣纯粹是技术性的——切片是否复制列表,以及答案是否在不同情况下有所不同。
python - Python 交换列表
在python中,当我将一个列表分配给另一个时,例如:
现在 b 和 a 指向同一个列表。现在考虑两个列表,
现在,它们如何像任何其他数据类型一样被交换并且最终不会都指向同一个列表?
python - 写一个生成器还是返回一个生成器?
在容器类中,当我想迭代它的项目(或者它的项目的转换,或者它的项目的子集)时,我可以编写一个生成器(like f
)或返回一个生成器(like g
):
我不需要通过send
.
显然,两种方式的行为相同(表面上)。
哪种方法更可取,为什么?
哪种方法会产生更少的开销或具有我没有发现的其他优势?
python - 同一个 Python 模块的两个实例?
我用一个函数创建了一个 Python 模块,它只打印“a!”。我打开了 Python 解释器并以 2 种不同的语法导入了模块
此时我将 func 更改为打印其他内容,然后再次评估
这当然是意料之中的,因为模块没有重新加载。然后我重新加载了模块并希望这两个函数都能更新,但是:
只有 a.func 似乎更新了。我一直认为 Python 只保留同一个模块的一个实例,但现在似乎有两个。为了验证我的声明,我做了进一步的测试,并在模块的顶层添加了一个打印语句,然后重新启动解释器并再次导入:
这让我更加困惑,因为我预计会看到两次“导入的模块”。我做的第三个实验是全局变量实验:
所以模块只有一个实例,但里面的对象有不同的实例?如何用这种行为实现Gevent 的猴子补丁?
python - python2不愿意释放内存吗?
我知道 python 有自己的内存管理实现,使用区域来处理不同大小的对象等等,尽管我还没有找到完整的文档。我仍然想了解幕后发生的事情。
背景是一个长时间运行的 python2 数据库应用程序,它似乎以某种方式泄漏内存,它运行在 64 位 linux 上。该应用程序每天都会从数据库中读取一些数据,仅用于读取行(使用 MySQLdb)的 RAM 使用量总计约为 3.5GB。大约有 350 万行,之后减少到 100 行,其余行超出范围(“释放”)。
但是 python-2.7 只释放了一小部分现在“未使用”的内存。我知道内存稍后会被重用,但我观察到不知何故,这个内存似乎“慢慢泄漏”。上面提到的 DB 应用程序每天都会读取大量数据。连续读取两次(或多次)只会为第一次读取分配内存,然后显然会重用该内存。但是让它运行几个小时,然后再次读取数据库数据会产生下一个 3+GB 的内存分配峰值(这又是永远不会释放的)。
为了添加更多背景信息(并且让事情变得更糟),我不得不说这个 DB 应用程序不是空闲的,而是永久执行任务。通过监控内存使用情况(nagios 性能数据),我非常确定如果没有这个特定的数据库查询,内存使用量永远不会攀升到 3.5GB RAM(甚至接近)。但是启用此查询会每天增加 3+GB RAM。有问题的查询主要返回唯一的整数和浮点数。
这是我开始怀疑python的主要原因。我觉得我已经阅读了大量信息,查看了 _PyObject_DebugMallocStats() 但不知道 python 决定保留几个千兆字节是什么(或为什么)。
它归结为一个非常简单的示例(不代表有关数据的真实生活情况,我知道 xrange()):
有趣的是,当我删除巨大的列表时,py3k 会释放内存。不仅是一小部分,而且几乎所有留下的内存使用量仅比开始时略高。
我已经用 memory_profiler 对此进行了调查(我猜它的作用并不比给定的 mem_usage() 函数多多少)而没有任何洞察力。我已经阅读了有关 gdb-heap 的信息,但到目前为止还无法正常工作。
我实际上不相信有解决方案(除了重新启动应用程序或减少从数据库读取的数据量)。但我真的很感激关于这个话题的任何见解。
编辑:
总结一下我的问题:为什么 python-2.7 保持分配这个内存?