具有类似 STL 接口的 Python 列表
Posted
技术标签:
【中文标题】具有类似 STL 接口的 Python 列表【英文标题】:Python lists with STL like interface 【发布时间】:2010-01-01 12:39:46 【问题描述】:我必须将 C++ STL 应用程序移植到 Python。我是一名 Python 新手,但已经编程了十多年。我对 STL 有丰富的经验,并且发现它让我对使用 C++ 着迷。这几天我一直在 Google 上搜索以下项目:
-
Python STL(希望利用我多年的 STL 经验)
Python 链表
Python 高级列表用法
Python 列表优化
Python 有序集
并且找到了关于上述主题的帖子,Python 列表上的教程绝对不是高级的,或者死胡同。我对自己没有成功感到非常惊讶,我想我只是因为过度工作和输入错误的搜索词而筋疲力尽!
(我的问题)我能否获得一个 Python STL 包装器,或者一个与 STL 类似的 Python 列表接口?如果没有,有人可以向我指出有关管理非常大的非平凡对象的排序集合的真正高级教程或论文吗?
附:我可以很容易地为一两次使用实现变通办法,但是如果管理层想要移植更多代码,我想准备好立即用等效的 Python 代码替换我找到的任何 STL 代码。是的,我已经测量并且确实需要完全优化代码!我不能只做多余的排序和搜索!
(ADDENDUM) 感谢您的回复,我已经检查了一些参考资料并且很高兴。回应这里的一些cmets:
1 - 它正在被移植到 python,因为管理层这么说,我会尽快让它不管 - 如果它没有坏,为什么要修复它?
2 - 非平凡对象的高级列表用法,我的意思是:许多不同的方式来排序和比较对象,而不是通过一个 cmp 方法。我想对列表进行拼接、排序、合并、搜索、插入、擦除和组合。我想要列表迭代器的列表,我想避免复制。
3 - 我现在知道内置列表实际上是数组,我应该寻找不同的 python 类。我想这是我困惑的根源。
4 - 当然,我正在学习以 Python 方式做事,但我也有截止日期。我正在移植的 STL 代码工作正常,我想尽可能少地更改它,因为这会引入错误。
感谢大家的意见,我真的很感激。
【问题讨论】:
如果您绝对需要完全优化的代码,那么为什么要将它从 C++ 移植到 Python? Python 比 C++ 慢得多。您不能期望获得类似的性能。 大写字母不会让你的想法更有说服力。 请不要全部大写。你有粗体和斜体——它们更适合强调。 试图在 Python 中重现 C++ 习语只会让你成为一个糟糕的 Python 程序员。阅读下面 Idan K 的回答,他是对的。 我没有得到反对票,赞成票。这是一个有效的问题,如果我们都知道如何处理我们不会提出问题的所有问题。 【参考方案1】:Python 的“列表”不是链接的列表——它们就像 Java ArrayLists 或 C++ 的 std::vector
s,也就是说,在较低级别的术语中,是一个可调整大小的紧凑指针数组.
Hettinger 的Core Python containers: under the hood 演示文稿是关于此类主题的一个很好的“高级教程”(URL 上的视频是意大利会议上的演示文稿,但它是英文的;另一个基本相同演讲的简短演示文稿是 @987654323 @)。
所以 Python 列表的性能特征本质上是 C++ 的 std::vector
的性能特征:Python 的 .append
和 C++ 的 push_back
一样,是 O(1),但“中间”的插入或删除是 O(N) .因此,保持列表排序(可以借助 Python 的标准库模块 bisect 中的函数轻松完成)代价高昂(如果项目随机到达和/或离开,则每次插入和删除都是 O(N),就像类似地维护std::vector
中的顺序。对于某些目的,例如优先级队列,您可能会摆脱“堆队列”,借助Python标准库模块heapq中的函数也很容易维护--但当然,它不能提供与完全排序的列表(或向量)相同的使用范围。
因此,对于在 C++ 中使用 std::set
的目的(并且依赖于它是有序的,即哈希集不会这样做——Python 的 set
s 是基于哈希的,而不是有序的)你最好避免使用 Python 内置容器,而使用 this module(如果您需要保持纯 Python)或 this one(它提供 AVL 树,而不是 RB 树,但编码为 C 实现Python 扩展等可能会提供更好的性能)如果 C 编码的扩展是可以的。
如果你最终使用了自己的模块(无论是纯 Python 还是 C 编码的),如果你愿意,你可以给它一个类似 STL 的单板/接口(@987654336@、.end
、通过递增而不是按照正常的 Python 行为,通过调用它们的 next
方法来高级迭代器对象,......),尽管它永远不会像语言的“随波逐流”那样执行(@ 987654339@ 语句经过优化,可以使用普通的 Python 迭代器,即带有 next
方法的迭代器,它比将有点尴尬的 while
包裹在非 Python 标准、类似 STL 的迭代器上要快)。
要为任何 Python 内置容器提供类似 STL 的饰面,您将产生大量的包装开销,因此性能损失可能相当大。如果您,如您所说,“确实需要完全优化的代码”,那么仅出于“语法便利”的目的使用这样的单板似乎是一个非常糟糕的选择。
Boost Python,封装了强大的 C++ Boost 库的 Python 扩展包,可能最适合您的目的。
【讨论】:
感谢您提供有用的信息,我发现上述链接非常有帮助。我想应用 STL 包装器的原因是为了保持代码的稳定性,即;不引入错误。 所以我希望你能很好地利用 Boost Python,否则我相信使用你想要的接口并且仍然获得最佳性能可能是不兼容的目标。【参考方案2】:如果我是你,我会花时间学习如何正确使用 Python 中可用的各种数据结构,而不是寻找与你在 C++ 中所知道的类似的东西。
您并不是在寻找花哨的东西,只是在使用一些数据结构。在这种情况下,我会将您推荐给Python's documentation on the subject。
以“Python”方式执行此操作将帮助您,更重要的是未来的维护者会想知道您为什么尝试在 Python 中编写 C++。
只是为了激发你的胃口,也没有理由更喜欢 STL 的风格而不是 Python(为了记录,我也是一个通晓 STL 的 C++ 程序员),考虑最琐碎的例子构造一个列表并遍历它:
Pythonic 方式:
mylist = [1, 2, 3, 4]
for value in mylist:
# playaround with value
Python 中的 STL 方式(我编造的,类似于 STL):
mylist = [1, 2, 3, 4]
mylistiter = mylist.begin()
while mylistiter != mylist.end():
value = mylistiter.item()
mylistiter.next()
【讨论】:
这不是更好吗?每当您使用 while 循环时,总是存在忽略或跳过增量语句的风险。 您的 for 语句不是有效的 Python。我的示例的全部目的是演示 STL 样式的迭代在 Python 中的外观。 STL 方式是不显式迭代。 我很欣赏你所说的,但我必须处理最后期限,而移植代码的最安全方法是坚持使用 STL 惯用语,因为这不会引入新的错误。我是一个真正的 Python 新手,当我有更多使用它的经验时,我会很乐意开始使用 Python Way(TM) 做事。在那之前,我不在乎成为一个糟糕的 Python 程序员,我只想按时交付功能正常、稳定、快速的代码。 @Dustin,camh - 我编写的代码只是为了说明一点,我不想用 for_each 和 lambdas 的例子使事情复杂化。此外,有时为简单的 for 循环编写函子是多余的。【参考方案3】:对于类似链表的操作,人们通常使用collections.deque
。
您需要哪些操作才能快速执行?平分?插入?
【讨论】:
【参考方案4】:我想说您的问题不仅仅是 STL 移植。由于通过 STL 绑定到 C++ 的 list、dict 和 set 数据结构是核心 Python 的本机,因此它们的用法被合并到常见的 Python 代码习语中。如果您想再给 Google 一次机会,请尝试查找“Python for C++ Programmers”的参考资料。您的点击之一将是this presentation by Alex Martelli。它有点过时了,早在 ought-3 的时候,但有一些基本的 Python 代码读取文本文件,以及使用 STL 的外观。
从那里,我建议您阅读这些 Python 功能:
迭代器 发电机 列表和生成器理解还有这些内置函数:
压缩包 地图一旦您熟悉了这些,那么您将能够在 STL 使用和 Python 内置数据结构之间构建自己的转换/映射。
正如其他人所说,如果您正在寻找一种“即插即用”的公式将 STL C++ 代码转换为 Python,那么您最终会得到一个糟糕的 Python。这种蛮力方法永远不会导致单行列表理解的强大、优雅和简洁。 (当我向我们的一位熟悉 Java 和 C++ 迭代器的经理介绍 Python 时,我就有了这样的经历。当我向他展示这段代码时:
numParams = 1000
paramRequests = [ ("EqptEmulator/ProcChamberI/Sensors",
"ChamberIData%d"%(i%250)) for i in range(numParams) ]
record.internalArray = [ParameterRequest(*pr) for pr in paramRequests]
我解释说这些替换了这段代码(或类似的东西,这可能是 C++ 和 Java API 的混搭,抱歉):
std::vector<ParameterRequest> prs = new std::vector<ParameterRequest>();
for (int i = 0; i<1000; ++i)
string idstr;
strstream sstr(idstr);
sstr << "ChamberIData" << (i%250);
prs.add(new ParameterRequest("EqptEmulator/ProcChamberI/Sensors", idstr));
record.internalArray = new ParameterRequest[prs.size];
prs.toArray(record.internalArray);
使用 C++ 时,您的一个直觉是不愿意从旧列表中创建新列表,而是在适当的位置更新或过滤列表。我们甚至在 Python 开发人员的许多论坛上看到这一点,他们询问如何在迭代列表时修改列表。在 Python 中,最好使用列表推导式从旧列表构建一个新列表。
allItems = [... some list of items, perhaps from a database query ...]
validItems = [it for it in allItems if it.isValid()]
相对于:
validItems = []
for it in allItems:
if it.isValid():
validItems.add(it)
或更糟:
# get list of indexes of items to be removed
removeIndexes = []
for i in range(len(allItems)):
if not allItems[i].isValid():
removeIndexes.add(i)
# don't forget to remove items in descending order, or later indexes
# will be invalidated by earlier removals
sort(removeIndexes,reverse=True)
# copy list
validItems = allItems[:]
# now remove the items from allItems
for idx in removeIndexes:
del validItems[i]
【讨论】:
感谢您的回复,我理解您和其他人对我的做法的反对意见,但面对顽固的管理,我正在尽力而为。他们说,“嘿,你,把它移植到 python 上!”我说,“我真的不知道 python。”,他们回答说“学习它并在 2 月之前移植它”。所以我真的不想用即插即用的方式替代 STL,因为这是我在不添加错误的情况下移植代码的最安全途径。在未来的版本中,在获得几个月的 python 经验后,我可能会开始用 python way(TM) 替换 STL 方式,但我觉得还没有能力这样做。【参考方案5】:Python STL(希望利用我多年的 STL 经验)- 从集合 ABC 开始了解 Python 有什么。 http://docs.python.org/library/collections.html
Python 链表。 Python 列表具有链表所需的所有功能。
Python 高级列表用法。这是什么意思?
Python 列表优化。这是什么意思?
Python 有序集。您在这里有多种选择;您可以发明自己的“有序集”作为丢弃重复项的列表。您可以继承 heapq 并添加丢弃重复项的方法:http://docs.python.org/library/heapq.html。
然而,在许多情况下,维护有序集的成本实际上是过高的,因为它只能在算法结束时被排序一次。在其他情况下,“有序集”实际上是一个 heapq——你从不需要类似集合的特性,只需要排序。
非平凡。 (我猜你所说的“非平凡”是什么意思)。所有 Python 对象都是等价的。没有“微不足道”与“非微不足道”的对象。它们都是一流的对象,并且在没有任何实际工作的情况下都可以具有“非平凡”的复杂性。这不是 C++ 中存在浮动的原始(非对象)值。 Python 中的一切都是对象。
管理层期望。 在大多数情况下,Python 中不存在 C++ 脑筋急转弯。以明显的方式使用明显的 Python 类,您将拥有更少的代码。代码量的减少是一大胜利。通常,将 C++ 转换为 Python 的管理原因是为了摆脱 C++ 的复杂性。
Python 代码将更加简单,使其更加可靠且易于维护。
虽然 Python 确实比 C++ 慢,但选择正确的算法和数据结构也确实可以显着提高性能。在一个基准测试中,有人发现 Python 实际上快 比 C,因为 C 程序的数据结构选择不当。
您的 C++ 可能有一个非常糟糕的算法,而您会看到 Python 的性能相当。
您的 C++ 程序也可能是 I/O 受限的,或者有其他限制会使 Python 以相当的速度运行。
【讨论】:
总的来说,我同意你的看法,但 C++ 确实有一些经过良好调整的容器和算法。您有更多机会做出最佳选择。也就是说,如果这是 Unknown 的动机,那么我真的认为转向 Python 是一个错误。 Python 列表没有您可能希望从链表中获得的所有功能。在 Python 列表中间插入一个元素是 O(n)。 而 C++ 与 Python 一样,对内置类型和用户定义类型的处理方式非常相似。一切都是 C++ 中的对象。 @Jason Orendorff:C++ 有非对象类型。 int、long、double、char 不是对象。 谢谢,但 C++ 程序运行良好,这就是重点。如果我可以进行仔细的翻译,那么我知道 Python 代码也可以很好地工作。如果我尝试以 python 方式(TM)做事,那么我可能会引入错误,特别是因为我上周开始学习 python!【参考方案6】:Python 的设计是有意为之“您可以只使用几个数据结构(数组和哈希表)来做任何您想做的事情,如果速度不够快,总会有 C”。
Python 的标准库没有像std::set
这样的排序列表数据结构。您可以download a red/black tree implementation 或自己滚动。 (对于小型数据集,仅使用列表并定期对其进行排序在 Python 中是完全正常的事情。)
滚动你自己的链表非常容易。
【讨论】:
以上是关于具有类似 STL 接口的 Python 列表的主要内容,如果未能解决你的问题,请参考以下文章
基本 STL:向量列表或列表向量,在这种特殊情况下哪个更好?