Python文字列は不変ではありませんか?では、なぜa +“” + bが機能するのでしょうか。


109

私の理解では、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
# !!!

Pythonは割り当てを妨げるべきではありませんか?私はおそらく何かを逃している。

何か案が?


55
文字列自体は不変ですが、ラベルは変更できます。
ミッチ

6
既存の変数に新しい値を割り当てることは完全に有効です。Pythonには定数がありません。これは、データ型の可変性から独立しています。
Felix Kling、2012

14
id()関数を見てみましょう。a割り当ての前後で異なるIDを持ち、異なるオブジェクトを指していることを示します。同様に、次のようなコードb = aを使用するab、同じオブジェクトが参照されていることを示す同じIDが表示されます。
DRH


デルナンからのリンクはまさに​​私が言及していたものです。
ミッチ

回答:


179

最初にa文字列「Dog」を指しています。次に、変数aを変更して、新しい文字列「Dog eatstreats」を指すようにしました。文字列「Dog」は実際には変更していません。文字列は不変であり、変数は好きなように指すことができます。


34
x = 'abc'のようなものを試してみると、さらに説得力があります。x [1] = Python replの 'x'
xpmatteo

1
内部についてもう少し詳しく知りたい場合は、私の答えをご覧ください。stackoverflow.com/a/40702094/117471
Bruno Bronosky

53

文字列オブジェクト自体は不変です。

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.

@jasonは、同じ種類の操作(変更可能なリスト)を試して、a.append(3)がa = a + "Foo"
jimifiki

1
@jimifikiはと同じでa.append(3) はありませんa = a + 3。それもありませんa += 3(インプレースの追加がに相当し.extendないように、.append)。

@delnanなど、何ですか?文字列とリストの動作が異なることを示すために、a = a + "Foo"はa.append(something)と同じであると想定できます。いずれにせよ、それは同じではありません。明らかに。a.append(something)の代わりにa.extend([something])を読んだ方が幸せでしたか?この文脈では大きな違いは見られません。しかし、おそらく何かが足りない。Thruthは、文脈に依存
jimifiki

@jimifiki:何のことをいっているのですか。+リストと文字列で同じように動作します-新しいコピーを作成して連結し、どちらのオペランドも変更しません。

6
これらすべてを取り除くための本当に重要な点は、文字列 append は不変であるため
Lily Chung

46

変数aはオブジェクト「Dog」を指しています。Pythonの変数はタグと考えるのが最善です。に変更a = "dog"したときと同じように、タグを別のオブジェクトに移動できますa = "dog eats treats"

ただし、不変性とは、タグではなくオブジェクトを指します。


あなたがしようとした場合a[1] = 'z'にする"dog""dzg"は、エラーになるだろう。

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

文字列はアイテムの割り当てをサポートしていないため、不変です。


19

メモリの場所自体を変更せずにメモリの場所に保持されている値を変更できる場合にのみ、何かが変更可能です。

コツは、変更前と変更後のメモリ位置が同じである場合、変更可能であるということです。

たとえば、リストは変更可能です。どうやって?

>> 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再割り当ての前後は異なるため、これは実際には変異していないが、変数を新しい場所にポイントしていることを証明します。これはその文字列を変更するのではなく、その変数を変更することです。


11

変数は、オブジェクトを指す単なるラベルです。オブジェクトは不変ですが、必要に応じて、ラベルを完全に異なるオブジェクトを指すようにすることができます。


8

考慮してください:

>>> 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>

変数に同じ値を2回格納しても、16進数のメモリの場所は変更されなかったことに注意してください。別の値を保存すると、それは変わりました。文字列は不変です。熱心さのためではなく、メモリ内に新しいオブジェクトを作成するというパフォーマンス上のペナルティを支払うためです。変数aは、そのメモリアドレスを指すラベルにすぎません。何かを指すように変更できます。


7

ステートメントa = a + " " + b + " " + cはポインタに基づいて分解できます。

a + " "は、a変更できないポイントを教えてくれ" "、現在のワーキングセットに追加します。

メモリ:

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

+ bb変更できないポイントを教えてくれ、それを現在のワーキングセットに追加します。

メモリ:

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"。これは、不変が意味するものです。ただし、メモリのそのセクションを指すラベルがあれば変更できます。


6
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

5

データとそれに関連付けられているラベルに違いがあります。例えばあなたがするとき

a = "dog"

データ"dog"が作成され、ラベルの下に配置されaます。ラベルは変更できますが、メモリにあるものは変更されません。データ"dog"はメモリに残っています(ガベージコレクターが削除するまで)。

a = "cat"

プログラムでa^が指すようになりました"cat"が、文字列"dog"は変更されていません。


3

Python文字列は不変です。ただし、aこれは文字列ではありません。文字列値を持つ変数です。文字列を変更することはできませんが、変数の値を新しい文字列に変更できます。


2

変数は、好きな場所を指すことができます。次の場合、エラーがスローされます。

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

2

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'

この例では、aに別の値を割り当てても変更されないことがわかります。新しいオブジェクトが作成されます。
また、変更することはできません。例:

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

エラーが発生します。


2

'mutable'は文字列の内容を変更できることを意味し、 'immutable'は余分な文字列を追加できないことを意味します。

写真校正のためにクリック


1

>>> a = 'dogs'

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

'dogs eat treats'

>>> print a

'dogs'

不変ですね。

変数変更の部分についてはすでに説明しました。


1
これは、Python文字列の可変性を証明したり反証したりするものではなく、replace()メソッドが新しい文字列を返すだけです。
ブレントHronik

1

あなたの例にこの追加を検討してください

 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

私がブログで見つけたより正確な説明の1つは次のとおりです。

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-n both /


1

上記の回答にもう少し追加します。

id 変数の再割り当て時に変化します。

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

つまりa、新しい文字列を指すように変数を変更しました。これで、2つの string(str)オブジェクトが存在します。

'initial_string'id= 139982120425648

そして

'new_string'id= 139982120425776

以下のコードを考えてみましょう:

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

さて、b指す'initial_string'と同じ持っているidようにa再割り当ての前に持っていたし。

したがって、は'intial_string'変異されていません。


0

要約:

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

これは不変であるため、Python 3のエラーです。そして、明らかに不変ではないので、Python 2のエラーではありません。


0

組み込み関数id()は、オブジェクトのIDを整数として返します。この整数は通常、メモリ内のオブジェクトの場所に対応しています。

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

139831803293008

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

139831803293120

文字列オブジェクトはPythonで不変であるため、最初に「a」は139831803293008のメモリロケーションに格納されます。参照を変更して再割り当てしようとすると、削除され、新しいメモリロケーションへのポインタになります(139831803293120)。


0
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

2
このコードはOPの問題を解決する可能性がありますが、コードがOPの問題にどのように対処するかについての説明を含めることをお勧めします。このようにして、将来の訪問者はあなたの投稿から学び、それを自分のコードに適用することができます。SOはコーディングサービスではなく、知識のリソースです。また、高品質で完全な回答が支持される可能性が高くなります。これらの機能は、すべての投稿が自己完結型であるという要件とともに、フォーラムとは異なるプラットフォームとしてのSOの強みの一部です。編集して追加情報を追加したり、ソースドキュメントで説明を補足したりできます
SherylHohman


-1

2つの文字列値を連結するだけです。(a)の値を変更することはありません。ちょうど今(a)「dogdog」値を持つ別のメモリブロックを表します。バックエンドでは、1つの変数が2つのメモリブロックを同時に表すことはありません。連結前の(a)の値は「犬」でした。しかし、その後(a)は「dogdog」を表しています。「dogdog」値を持つブロック。そして「犬」は代表です。(b)によって "dog"は、(b)が "dog"を表すまで、ガベージ値としてカウントされません。

混乱は、同じ変数名でバックエンドのメモリブロック(データまたは情報を含む)を表すことです。


-2

numpy配列を不変にして、最初の要素を使用できます。

numpyarrayname[0] = "write once"

次に:

numpyarrayname.setflags(write=False)

または

numpyarrayname.flags.writeable = False
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.