【问题标题】:Python slice [:] inconsistent behaviorPython slice [:] 不一致的行为
【发布时间】:2019-01-24 23:46:25
【问题描述】:

在 Python 中,为什么 [:] 切片操作的行为不一致?
它对列表和字符串的行为不同。

对于列表,它给出一个副本列表对象,对于字符串,它给出相同的字符串对象。

我觉得这令人困惑,违反直觉。有什么方法可以解释/证明这一点?

    >>> s = "1234"
    >>> s is (s[:2] + s[2:])
    False
    >>> s is s[:]
    True
    >>> lst = [1,2,3,4]
    >>> lst is lst[:]
    False
    >>> lst is (lst[:2] + lst[2:])
    False

我觉得这部分令人困惑。我希望这会返回False

>>> s is s[:]
True

另外,我希望这两个返回相同的结果False,但事实并非如此。

>>> s is (s[:2] + s[2:])
False
>>> s is s[:]
True

有什么想法吗?

【问题讨论】:

  • 基本上,interned 在 Python 中是无法保证的。这意味着您不能期望相同的字符串指向同一个对象。
  • @Sraw 我问的正好相反。
  • 如果您的程序曾经关心不可变对象的身份,那么您可能做错了什么。对象标识对于可变对象很重要,但各种对象重用优化对于不可变对象都是安全的。
  • @user2357112 我明白了,但是......好吧,行为似乎仍然不一致。为什么s is s[:] 返回True。我觉得这很奇怪,就我一个人吗?
  • @peter.petrov 这是一个实现细节,毫无疑问,str 对象的“整个切片”被优化为简单地返回 str 对象本身。为什么这很重要?

标签: python python-3.x slice


【解决方案1】:

对于大多数内置类型,Python 尝试确保切片的(浅)突变不会影响切片所来自的对象。

对于列表,这需要一个副本,否则改变 l[:] 会改变 l

对于字符串,字符串不支持变异,因此即使s[:] is s ,诸如“变异s[:] 不会影响s”之类的语句也是空洞的。无需制作副本,因此为了节省时间和内存,实现不制作副本。这是一个实现细节,而不是语言保证;实现复制是完全有效的。

(您可能认为涉及实习,但事实并非如此。此对象重用优化与实习完全无关。即使对于非实习字符串也会发生。如果您愿意,可以查看the implementation。)


一般来说,如果您的程序关心不可变对象的身份,那么您可能做错了什么。对象标识对可变对象很重要,Python 确保可变对象的标识是可预测的。对于不可变对象,对象标识的影响很小,Python 应用了许多对象重用实现,这些实现打破了许多关于对象标识的乐观假设。

【讨论】:

    【解决方案2】:

    短字符串是interned,因此一百万个“测试”实例不会占用一百万个字符串的存储空间。这是安全的,因为 Python 的字符串是不可变的:如果两个字符串一次相等,则可以保证它们在其余下的生命周期中都是相等的。

    这是另一个用相同内存对象表示的短字符串示例:

    >>> a = 'foo'
    >>> b = 'foo'
    >>> a is b
    True
    

    【讨论】:

    • 虽然实习是一回事,但这里不涉及。 (另外,Python 的实习标准实际上并不涉及长度。)
    • 有多短?我刚刚尝试了一个 200 个字符的字符串,它仍然说 a is a[:]True
    • 涉及到。 s[:] 是“制作 s 的副本”的简写,通过指针操作最有效地完成。 s[:2] + s[2:] 的不同之处在于它是从片段中构建一个字符串,即使结果恰好已经在内存中,Python 也很难在事后确定这一点。不过,我可能对字符串长度位有误。
    猜你喜欢
    • 1970-01-01
    • 2013-04-01
    • 2020-08-20
    • 2020-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-20
    相关资源
    最近更新 更多