Python 字符串标识:“is”和“in”语句



我在让它工作时遇到了一些问题:

# Shortened for brevity
def _coerce_truth(word):
    TRUE_VALUES = ('true','1','yes')
    FALSE_VALUES = ('false','0','no')
    _word = word.lower().strip()
    print "t" in _word
    if _word in TRUE_VALUES:
        return True
    elif _word in FALSE_VALUES:
        return False

我发现:

In [20]: "foo" is "Foo".lower()
Out[20]: False
In [21]: "foo" is "foo".lower()
Out[21]: False
In [22]: "foo" is "foo"
Out[22]: True
In [23]: "foo" is "foo".lower()
Out[23]: False

这是为什么呢?我知道身份与平等不同,但身份是什么时候形成的?语句 22 应该False,除非由于字符串的静态性质,id == eq。在这种情况下,我对语句 23 感到困惑。

请提前解释和感谢。

问:"身份是什么时候形成的?"

一个。创建对象时。

你看到的实际上是Cpython的实现细节——它缓存小字符串并重用它们以提高效率。 其他有趣的案例是:

"foo" is "foo".strip()  # True
"foo" is "foo"[:]       # True

最终,我们看到的是字符串文字"foo"已被缓存。 每次键入 "foo" 时,您都在引用内存中的同一对象。 但是,一些字符串方法会选择始终创建新对象(如.lower()),如果方法没有进行任何更改(如.strip()),一些方法会巧妙地重用输入字符串。


这样做的一个好处是,字符串相等可以通过指针比较(极快)来实现,如果指针比较为 false,则逐字符比较。 如果指针比较为 True,则可以避免逐字符比较。

至于isin之间的关系:

__contains__方法(位于运算符后面in)用于查找匹配项时tuplelist,首先检查标识,如果失败,则检查相等性。即使对象与自身不相等,这也将为您提供理智的结果:

>>> x = float("NaN")
>>> t = (1, 2, x)
>>> x in (t)
True
>>> any(x == e for e in t) # this might be suprising
False

假设您有一堆方法可以创建字符串值为 'foo' 的对象:

def f(s): return s.lower()
li=['foo',                     # 0
    'foo'.lower(),             # 1
    'foo'.strip(),             # 2
    'foo',                     # 3
    'f'+'o'*2,                 # 4
    '{}'.format('foo'),        # 5
    'f'+'o'+'o',               # 6
    intern('foo'.lower()),     # 7
    'foo'.upper().lower(),     # 8
    f('foo'),                  # 9
    'FOO'.lower(),             # 10
    'foo',                     # 11
    format('foo'),             # 12
    '%s'%'foo',                # 13
    '%s%s'%('fo','o'),         # 14
    'f'   'oo'                 # 15
]

is 仅当对象具有相同的 id 时解析为 True。您可以对此进行测试,并查看哪些字符串对象在运行时已暂留到同一个不可变字符串中:

def cat(l):
    d={}
    for i,e in enumerate(l):
        k=id(e)
        d.setdefault(k,[]).append(i)
    return 'n'.join(str((k,d[k])) for k in sorted(d)) 

指纹:

(4299781024, [0, 2, 3, 6, 7, 11, 12, 13, 15])
(4299781184, [5])
(4299781784, [1])
(4299781864, [4])
(4299781904, [9])
(4299782944, [8])
(4299783064, [10])
(4299783144, [14])

您可以看到大多数解析(或被扣留)到同一个字符串对象,但有些则没有。这取决于实现。

您可以使用函数 intern 使它们都成为相同的字符串对象:

print cat([intern(s) for s in li]) 

指纹:

(4299781024, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])

最新更新