【问题标题】:How does str(list) work?str(list) 是如何工作的?
【发布时间】:2015-07-18 11:54:21
【问题描述】:

为什么str(list) 会返回我们在控制台上看到的列表? str(list) 是如何工作的? (对str(list) 的 CPython 代码有任何引用吗?

>>> x = ['abc', 'def', 'ghi']
>>> str(x)
"['abc', 'def', 'ghi']"

要从 str(list) 取回原始列表,我必须:

>>> from ast import literal_eval
>>> x = ['abc', 'def', 'ghi']
>>> str(x)
"['abc', 'def', 'ghi']"
>>> list(str(x))
['[', "'", 'a', 'b', 'c', "'", ',', ' ', "'", 'd', 'e', 'f', "'", ',', ' ', "'", 'g', 'h', 'i', "'", ']']
>>> literal_eval(str(x))
['abc', 'def', 'ghi']

为什么list(str(list)) 不将str(list) 转回原来的列表?

或者我可以使用:

>>> eval(str(x))
['abc', 'def', 'ghi']

literal_evaleval 一样吗? eval 可以安全使用吗?

我可以执行多少次以下操作?如果继续执行str(list(str(list)))),代码会中断吗? 例如

>>> x = 'abc'
>>> list(x)
['a', 'b', 'c']
>>> str(list(x))
"['a', 'b', 'c']"
>>> list(str(list(x)))
['[', "'", 'a', "'", ',', ' ', "'", 'b', "'", ',', ' ', "'", 'c', "'", ']']
>>> str(list(str(list(x))))
'[\'[\', "\'", \'a\', "\'", \',\', \' \', "\'", \'b\', "\'", \',\', \' \', "\'", \'c\', "\'", \']\']'
>>> list(str(list(str(list(x)))))
['[', "'", '[', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", 'a', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", ',', "'", ',', ' ', "'", ' ', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", 'b', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", ',', "'", ',', ' ', "'", ' ', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", 'c', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", ']', "'", ']']
>>> str(list(str(list(str(list(x))))))
'[\'[\', "\'", \'[\', "\'", \',\', \' \', \'"\', "\'", \'"\', \',\', \' \', "\'", \'a\', "\'", \',\', \' \', \'"\', "\'", \'"\', \',\', \' \', "\'", \',\', "\'", \',\', \' \', "\'", \' \', "\'", \',\', \' \', \'"\', "\'", \'"\', \',\', \' \', "\'", \'b\', "\'", \',\', \' \', \'"\', "\'", \'"\', \',\', \' \', "\'", \',\', "\'", \',\', \' \', "\'", \' \', "\'", \',\', \' \', \'"\', "\'", \'"\', \',\', \' \', "\'", \'c\', "\'", \',\', \' \', \'"\', "\'", \'"\', \',\', \' \', "\'", \']\', "\'", \']\']'
>>> list(str(list(str(list(str(list(x)))))))
['[', "'", '[', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", '[', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", ',', "'", ',', ' ', "'", ' ', "'", ',', ' ', "'", '"', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", '"', "'", ',', ' ', "'", ',', "'", ',', ' ', "'", ' ', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", 'a', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", ',', "'", ',', ' ', "'", ' ', "'", ',', ' ', "'", '"', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", '"', "'", ',', ' ', "'", ',', "'", ',', ' ', "'", ' ', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", ',', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", ',', "'", ',', ' ', "'", ' ', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", ' ', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", ',', "'", ',', ' ', "'", ' ', "'", ',', ' ', "'", '"', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", '"', "'", ',', ' ', "'", ',', "'", ',', ' ', "'", ' ', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", 'b', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", ',', "'", ',', ' ', "'", ' ', "'", ',', ' ', "'", '"', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", '"', "'", ',', ' ', "'", ',', "'", ',', ' ', "'", ' ', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", ',', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", ',', "'", ',', ' ', "'", ' ', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", ' ', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", ',', "'", ',', ' ', "'", ' ', "'", ',', ' ', "'", '"', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", '"', "'", ',', ' ', "'", ',', "'", ',', ' ', "'", ' ', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", 'c', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", ',', "'", ',', ' ', "'", ' ', "'", ',', ' ', "'", '"', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", '"', "'", ',', ' ', "'", ',', "'", ',', ' ', "'", ' ', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", ']', "'", ',', ' ', '"', "'", '"', ',', ' ', "'", ']', "'", ']']

【问题讨论】:

  • 您似乎期望从列表创建字符串是可往返的。这不是故意的;列表不是最终用户可展示的对象,您会得到与repr(listobject) 相同的输出;仅供开发者使用的调试信息。
  • 列表根本没有__str__方法,它的tp_str槽实际上是空的。因此,它的tp_repr 插槽用于获取列表及其项目(包括循环对象)的repr 表示:hg.python.org/cpython/file/e8783c581928/Objects/…

标签: python string list eval python-internals


【解决方案1】:

嗯,你一共有4个问题,让我们一一来吧。

1.为什么str(list) 会返回我们在控制台上看到list 的方式? str(list) 是如何工作的?

什么是str()__str__()

str() 可调用对象仅返回 可打印 形式的对象!来自docs

str(object) 并不总是试图返回一个字符串 eval() 可以接受;它的目标是返回一个可打印的字符串。

每当您在对象上调用str() 时,都会调用类中的__str__() 函数。再次来自documentation

object.__str__(self)

str() 内置函数和print 语句调用以计算对象的“非正式”字符串表示。

什么是list 可调用对象?

list() 可调用是从作为参数传递的迭代创建一个列表。再次来自docs

返回一个list,其项目与 可迭代的项目

因此,str(list) 为您提供了一个可打印的表单,list(str(list)) 将遍历该字符串。也就是说,list(str(list)) 将为您提供传递参数的可打印形式的各个字符的列表。

嵌套调用之间的小演练,

给定列表,l = ['a','b'] (抱歉举了一个比你的问题更小的例子)

当您调用str(l) 时,它会返回列表l 的可打印形式,即"['a','b']"

现在您可以清楚地看到"['a','b']" 是一个字符串,并且确实是一个iterable。现在,当您在此(即list("['a','b']"))上调用list 时,您会得到一个奇怪的列表,例如['[', "'", 'a', "'", ',', "'", 'b', "'", ']']为什么会发生这种情况? 发生这种情况是因为字符串会遍历其字符,您可以使用虚拟字符串进行测试,

>>> 'dummy'
'dummy'
>>> list('dummy')
['d', 'u', 'm', 'm', 'y']

因此,当您在字符串上调用 list 时,您会得到一个字符列表。请再次注意,当您在list('dummy') 上调用str() 时,您将无法取回原始字符串'dummy',因此您将不得不再次使用join!因此,调用相同的函数将不会让您恢复原来的对象!

那么,在列表上调用str() 会调用列表的内置__str__() 方法吗?

答案是否定的!

当您在列表中调用str() 时,内部会发生什么?

每当您在列表对象上调用str() 时,遵循的步骤是

  1. 调用每个列表元素的repr()
  2. 在前面添加一个花哨的[,在列表末尾添加另一个]
  3. 用逗号将它们全部连接起来。

cpython on github中list对象的源码可以看到。 翻阅hg.python中cpython的源码,比较清楚,可以看到以下三个厘米。 (感谢 Ashwini 提供该特定 code 的链接)

/* Do repr() on each element.  Note that this may mutate the list,
   so must refetch the list size on each iteration. */ line (382)

/* Add "[]" decorations to the first and last items. */ line (398)

/* Paste them all together with ", " between. */ line (418)

这些与我上面提到的点相对应。

现在repr() 是什么?

repr() 打印所有对象的字符串表示。再次来自documentation

返回一个包含对象可打印表示的字符串。

还要注意这句话!

对于许多类型,此函数会尝试返回一个字符串 当传递给eval() 时会产生一个具有相同值的对象, 否则表示是用尖括号括起来的字符串 包含对象类型的名称以及 附加信息通常包括姓名和地址 对象。

现在你的第二个问题,

2。为什么list(str(list)) 不将str(list) 转回原来的列表?

在内部,str(list) 实际上创建了列表对象的repr() 表示。因此,要在列表上调用str 后返回列表,您实际上需要对其执行eval 而不是list 调用。

解决方法

但我们都知道eval is evil,那么解决方法是什么?

1。使用literal_eval

第一个解决方法是使用ast.literal_eval。这就引出了你的第三个问题,

3. literal_eval()eval() 一样吗? eval() 可以安全使用吗?

ast.literal_eval() 是安全的 unlike eval() 函数。文档本身提到它是安全的——

安全地计算表达式节点或包含 Python 文字或容器显示的字符串

2。使用字符串函数和内置函数

另一种解决方法可以使用str.split()

>>> x = ['abc', 'def', 'ghi']
>>> a = str(x)
>>> a[2:-2].split("', '")
['abc', 'def', 'ghi']

这只是对字符串列表执行此操作的一种简单方法。对于整数列表,您需要map

>>> x = [1,2,3]
>>> a =str(x)
>>> list(map(int,a[1:-1].split(', '))) # No need for list call in Py2
[1, 2, 3]

因此,与literal_eval 不同,这些都是简单的技巧,因为您知道列表的元素。如果它们本质上是异构的,例如[1, "a", True],那么您将不得不遍历拆分列表并发现元素类型,然后对其进行转换并将转换后的元素附加到最终列表中。

另一个失败的地方是字符串本身包含引号字符。正如nneonneocomment 中提到的那样

str.split 解决方案非常脆弱,如果输入包含例如包含", "、元组或其他列表的字符串……最好使用ast.literal_eval,因为这样可以处理语法的所有细节。

对于你的最后一个问题,

4.如果你一次又一次地str(list(str(list)))),代码是否会中断?

不是真的。每次您创建 liststr 然后再次获得它的可打印版本时,输出将变得越来越长。该限制仅是您的物理机的限制。 (每一步字符串长度乘以 5 就会很快达到。)

【讨论】:

  • 关于当您在list 上调用str 时内部会发生什么? 部分,为什么不在 之前执行3 >2 喜欢', '.join(i.__repr__() for i in l).join('[]')?
  • @KevinGuan 1. 效率 2. 简单。一次将括号添加到第一个和最后一个元素更容易。添加后,各个元素可以直接传递给_PyString_Join 并返回结果。正如您在示例中看到的那样,您是joining 两次。平均而言,这种方法效果更好。
  • str.split 解决方案非常脆弱,如果输入包含例如包含", "、元组或其他列表的字符串……最好使用ast.literal_eval,因为这样可以处理语法的所有细节。
【解决方案2】:

python 中的str() 函数用于将值转换为字符串。 str()list 所做的简单回答是,它创建了列表的字符串表示形式(方括号和所有)。

至于list(str(list)),您所做的只是告诉python 将原始列表转换为字符串,然后您将该字符串拆分并将其放入列表中,以便每个索引都有一个字符。因此,您可以随意嵌套liststr 调用(假设您的计算机有足够的内存)。

【讨论】:

    【解决方案3】:

    您似乎期望从列表创建字符串是可往返的。这不是故意的;列表不是最终用户可呈现的对象,您会得到与repr(listobject) 相同的输出;仅供开发者使用的调试信息。

    list() 可调用从任意可迭代对象创建一个新的列表对象;当您这样做时,Python 字符串可以迭代生成单个字符,list(stringobject) 总是生成一个包含单个字符的列表。

    因此,list() 永远不会尝试将字符串参数解释为 Python 语法;如果原始列表包含没有 Python 文字符号的对象,这样做甚至都行不通。举个例子:

    >>> def foo(): return 'bar'
    ... 
    >>> alist = [foo]
    >>> alist
    [<function foo at 0x106c748c0>]
    

    您不能获取调试字符串输出并将其转回原始列表,特别是如果您在 Python 解释器中运行它,甚至没有定义这样的函数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-07-07
      • 2021-12-18
      • 1970-01-01
      • 2011-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多