Python 字符串不是一成不变的吗? 那么为什么 a + “”+ b 可以工作呢?

我的理解是 Python 字符串是不可变的。

我试了下面的代码:

a = "Dog"
b = "eats"
c = "treats"


print a, b, c
# Dog eats treats


print a + " " + b + " " + c
# Dog eats treats


print a
# Dog


a = a + " " + b + " " + c
print a
# Dog eats treats
# !!!

难道巨蟒不应该阻止这次任务吗? 我可能漏掉了什么。

知道吗?

151967 次浏览

字符串对象本身是不可变的。

指向字符串的变量 a是可变的。

考虑一下:

a = "Foo"
# a now points to "Foo"
b = a
# b points to the same "Foo" that a points to
a = a + a
# a points to the new string "FooFoo", but b still points to the old "Foo"


print a
print b
# Outputs:


# FooFoo
# Foo


# Observe that b hasn't changed, even though a has.

变量只是指向对象的标签。该对象是不可变的,但如果愿意,可以让标签指向完全不同的对象。

第一个 a指向字符串“ Dog”。然后,您更改变量 a以指向一个新的字符串“ Dog eat ats”。你不是真的把字符串“ Dog”变异了吧。字符串是不可变的,变量可以指向任何他们想要的。

Python 字符串是不可变的。但是,a不是字符串: 它是一个具有字符串值的变量。您不能对字符串进行变异,但可以将变量的值更改为新的字符串。

数据和与其相关联的标签之间存在差异

a = "dog"

创建数据 "dog"并将其置于标签 a下。标签可以改变,但内存中的内容不会改变。数据 "dog"将仍然存在于内存中(直到垃圾收集器将其删除)

a = "cat"

在程序 a中,现在 ^ 指向 ^ "cat",但字符串 "dog"没有改变。

l = [1,2,3]
print id(l)
l.append(4)
print id(l) #object l is the same


a = "dog"
print id(a)
a = "cat"
print id(a) #object a is a new object, previous one is deleted

语句 a = a + " " + b + " " + c可以根据指针进行分解。

a + " "说,给我什么 a指向,这是不能改变的,并添加 " "到我当前的工作集。

记忆力:

working_set = "Dog "
a = "Dog"
b = "eats"
c = "treats"

+ b说,给我什么 b指向,这是不能改变的,并添加到当前的工作集。

记忆力:

working_set = "Dog eats"
a = "Dog"
b = "eats"
c = "treats"

+ " " + c表示将 " "添加到当前集合。然后给我什么 c指向,这是不能改变的,并添加到当前工作集。 记忆力:

working_set = "Dog eats treats"
a = "Dog"
b = "eats"
c = "treats"

最后,a =说将我的指针设置为指向结果集。

记忆力:

a = "Dog eats treats"
b = "eats"
c = "treats"

"Dog"被回收,因为没有更多的指针连接到它的内存块。我们从未修改驻留的内存部分 "Dog",这就是所谓的不可变。但是,我们可以更改指向该内存段的标签(如果有的话)。

变量 a 指向对象“ Dog”。最好将 Python 中的变量视为标记。您可以将标记移动到不同的对象,这就是您将 a = "dog"改为 a = "dog eats treats"时所做的操作。

但是,不可变性指的是对象,而不是标记。


如果你尝试 a[1] = 'z'"dog"变成 "dzg",你会得到错误:

TypeError: 'str' object does not support item assignment"

因为字符串不支持项分配,所以它们是不可变的。

总结:

a = 3
b = a
a = 3+2
print b
# 5

不是一成不变的:

a = 'OOP'
b = a
a = 'p'+a
print b
# OOP

不变的:

a = [1,2,3]
b = range(len(a))
for i in range(len(a)):
b[i] = a[i]+1

这在 Python3中是一个错误,因为它是不可变的。而不是 Python2中的错误,因为它显然不是不可变的。

只有当我们能够在不更改内存位置本身的情况下更改保存在内存位置中的值时,某些内容才是可变的。

诀窍是: 如果您发现更改之前和之后的内存位置是相同的,那么它是可变的。

例如,list 是可变的,怎么做呢?

>> a = ['hello']
>> id(a)
139767295067632


# Now let's modify
#1
>> a[0] = "hello new"
>> a
['hello new']
Now that we have changed "a", let's see the location of a
>> id(a)
139767295067632
so it is the same as before. So we mutated a. So list is mutable.

字符串是不可变的,我们怎么证明呢?

> a = "hello"
> a[0]
'h'
# Now let's modify it
> a[0] = 'n'
----------------------------------------------------------------------

我们得到

TypeError: ‘ str’对象不支持项分配

所以我们没能修改字符串,这意味着字符串是不可修改的。

在重新分配时,将变量更改为指向新位置本身。在这里,您没有对字符串进行变异,而是对变量本身进行了变异。以下是你正在做的事情。

>> a = "hello"
>> id(a)
139767308749440
>> a ="world"
>> id(a)
139767293625808

重新分配之前和之后的 id是不同的,所以这证明了您实际上并没有发生变化,而是将变量指向新的位置。不是变更字符串,而是变更变量。

>>> a = 'dogs'

>>> a.replace('dogs', 'dogs eat treats')

'dogs eat treats'

>>> print a

'dogs'

永恒不变,不是吗!

变量更改部分已经讨论过了。

考虑一下:

>>> a='asdf'
>>> a.__repr__
<method-wrapper '__repr__' of str object at 0x1091aab90>
>>> a='asdf'
>>> a.__repr__
<method-wrapper '__repr__' of str object at 0x1091aab90>
>>> a='qwer'
>>> a.__repr__
<method-wrapper '__repr__' of str object at 0x109198490>

注意,当我在变量中存储相同的值两次时,十六进制内存位置没有改变。当我存储不同的值时,它确实发生了变化。字符串是不可变的。不是因为狂热,而是因为在内存中创建新对象会带来性能损失。变量 a只是一个指向该内存地址的标签。它可以被改变,以指向任何东西。

变量可以指向任何他们想要的地方。 如果执行以下操作,将引发错误:

a = "dog"
print a                   #dog
a[1] = "g"                #ERROR!!!!!! STRINGS ARE IMMUTABLE

考虑一下这个例子

 a = "Dog"
b = "eats"
c = "treats"
print (a,b,c)
#Dog eats treats
d = a + " " + b + " " + c
print (a)
#Dog
print (d)
#Dog eats treats

我在一个博客中找到的一个更精确的解释是:

在 Python 中,(几乎)所有东西都是一个对象。在 Python 中,我们通常称为“变量”的东西更恰当地称为名称。同样,“赋值”实际上是名称与对象的绑定。每个绑定都有一个定义其可见性的作用域,通常是名称起源的块。

例如:

some_guy = 'Fred'
# ...
some_guy = 'George'

当我们稍后说 some _ guy = ‘ George’时,包含‘ Fred’的字符串对象不受影响。我们只是更改了 some _ guy 名称的绑定。但是,我们没有更改“ Fred”或“ George”字符串对象。在我们看来,他们可能会无限期地活下去。

链接到博客: https://jeffknupp.com/blog/2012/11/13/is-python-callbyvalue-or-callbyreference-neither/

您可以使一个 numpy 数组成为不可变的,并使用第一个元素:

numpyarrayname[0] = "write once"

然后:

numpyarrayname.setflags(write=False)

或者

numpyarrayname.flags.writeable = False

对上面提到的答案再加一点。

变量的 id在重新分配时发生变化。

>>> a = 'initial_string'
>>> id(a)
139982120425648
>>> a = 'new_string'
>>> id(a)
139982120425776

这意味着我们已经改变了变量 a来指向一个新的字符串。现在有了 string(str)对象:

'initial_string'id = 139982120425648

还有

'new_string'id = 139982120425776

考虑下面的代码:

>>> b = 'intitial_string'
>>> id(b)
139982120425648

现在,b指向 'initial_string',并具有与 a在重新分配之前相同的 id

因此,'intial_string'没有发生突变。

Python 字符串对象是不可变的。 例如:

>>> a = 'tanim'
>>> 'Address of a is:{}'.format(id(a))
'Address of a is:64281536'
>>> a = 'ahmed'
>>> 'Address of a is:{}'.format(id(a))
'Address of a is:64281600'

在这个例子中,我们可以看到,当我们赋不同的值在一个它不修改。一个新的对象被创建。
而且无法修改。 示例: < br/>

  >>> a[0] = 'c'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
**TypeError**: 'str' object does not support item assignment

出现错误。

‘ mutable’意味着我们可以改变字符串的内容, ‘ immutable’意味着我们不能添加额外的字符串。

click for photo proof

这幅图给出了答案,请看。

enter image description here

内置函数 id()返回对象的整数标识。这个整数通常对应于对象在内存中的位置。

\>>a='dog'
\>>print(id(a))


139831803293008


\>>a=a+'cat'
\>>print(id(a))


139831803293120

最初,‘ a’存储在139831803293008内存位置,因为在 python 中字符串对象是不可变的,如果你试图修改和重新分配引用,将被删除,并将成为一个指向新内存位置的指针(139831803293120)。

我们只是将两个字符串值连接起来。我们从不改变(a)的值。刚才(a)表示另一个具有“ dogdog”值的内存块。因为在后端,一个变量永远不会同时代表两个内存块。连接之前(a)的值是“ dog”。但是在那之后(a)表示“ dogdog”,因为现在(a)在后端 rep 中表示具有“ dogdog”值的块。而“ dog”是(b)的代表,在(b)表示“ dog”之前,“ dog”不作为垃圾值计算。

令人困惑的是,我们使用相同的变量名称来表示后端中的内存块(包含数据或信息)。

a = 'dog'
address = id(a)
print(id(a))


a = a + 'cat'
print(id(a))      #Address changes


import ctypes
ctypes.cast(address, ctypes.py_object).value    #value at old address is intact