2011-09-14 8 views
16

私はもともとPythonが純粋な参照渡し言語であると考えました。Pythonリストと各アクセス(組み込みリストの検索/置換)

C/C++からはメモリ管理について考えることはできませんが、私の頭の中に置くのは難しいです。だから、私はそれをJavaの観点から考えて、プリミティブ以外のすべてを、参照渡しとして考えようとしています。

問題:ユーザー定義のクラスのインスタンスを含むリストがあります。

は、私が使用した場合のために、各構文、すなわち:

for member in my_list: 
    print(member.str); 

memberオブジェクトへの実際の参照の等価のですか?

i = 0 
while i < len(my_list): 
    print(my_list[i]) 
    i += 1 

私は置き換えを行うに探していたときに、それが動作しないため、つまり、これは動作しません、それはないと思う::

それはやっての同等

です

for member in my_list: 
    if member == some_other_obj: 
     member = some_other_obj 

単純なリスト内での検索と置換。それはfor-eachループで行うことができますか?それ以外の場合は、ランダムアクセス構文(角括弧)を使用するだけで済むのですか、または動作しません。エントリを削除して新しいエントリを挿入する必要がありますか?私は:

i = 0 
for member in my_list: 
    if member == some_other_obj: 
     my_list.remove(i) 
     my_list.insert(i, member) 
    i += 1 
+0

リストの反復中に値はコピーされません。理由は[オブジェクトが関数にパラメータとして渡されたときにオブジェクトがコピーされない理由]と似ています(http://stackoverflow.com/questions/575196/in-python-why-can-a-function-modify-some-arguments-発呼者によって知覚される - b)。 – jfs

答えて

35

コメントは、Python変数の私自身の理解の改善につながったので、これは良いとされています。

コメントに記載されているように、for member in my_listなどのようなリストをループすると、member変数は、連続する各リスト要素にバインドされます。ただし、ループ内でその変数を再割り当てしても、リスト自体には直接影響しません。たとえば、次のコードは、リストは変更されません。

my_list = [1,2,3] 
for member in my_list: 
    member = 42 
print my_list 

出力:あなたは不変の型を含むリストを変更したい場合は

[1、2、3]

my_list = [1,2,3] 
for ndx, member in enumerate(my_list): 
    my_list[ndx] += 42 
print my_list 

出力:

、あなたのような何かをする必要があり

[43、44、45]

リストは変更可能なオブジェクトが含まれている場合は、直接現在のmemberオブジェクトを変更することができます

class C: 
    def __init__(self, n): 
     self.num = n 
    def __repr__(self): 
     return str(self.num) 

my_list = [C(i) for i in xrange(3)] 
for member in my_list: 
    member.num += 42 
print my_list 

[42、43、44]

リストのオブジェクトを変更するだけで、まだリストは変更されていないことに注意してください。

Naming and Bindingを読むと便利です。

+0

それはリストメンバーのコピーとして再定義されているのは残念です。私はそれを設定しているかのように、参照として表示するほうがはるかに有用であると信じています。それはおそらく何らかの方法で構造を操作することでしょう。 'idxの範囲(0、len(my_list)): my_list [idx] = new_obj' これは私の好みに適しています。ありがとうございました。 – Syndacate

+0

-1: 'member'は値のコピーではありません。 http://ideone.com/y7A9M – jfs

+0

@Syndacate:C++がこのようなことをどのように扱うのかよく分かりませんが、各ループ(またはその専門用語の強化されたforループ)のJavaのバージョンは、ほとんど効果がありません。 – GreenMatt

6

あなたはそこに何かを置き換えることができます。

>>> foo = ['a', 'b', 'c', 'A', 'B', 'C'] 
>>> for index, item in enumerate(foo): 
...  print(index, item) 
... 
(0, 'a') 
(1, 'b') 
(2, 'c') 
(3, 'A') 
(4, 'B') 
(5, 'C') 
>>> for index, item in enumerate(foo): 
...  if item in ('a', 'A'): 
...   foo[index] = 'replaced!' 
... 
>>> foo 
['replaced!', 'b', 'c', 'replaced!', 'B', 'C'] 

あなたがリストのコピーを反復処理する必要がリストから何かを削除したい場合は、あなたが反復処理されている何かのサイズを変更しようとしていることから、他のエラーを取得することに注意してください。これはスライスで非常に簡単に行うことができます。

間違っ:私たちはその上に反復として、我々はリストのサイズを変更しているため

>>> foo = ['a', 'b', 'c', 1, 2, 3] 
>>> for item in foo: 
...  if isinstance(item, int): 
...   foo.remove(item) 
... 
>>> foo 
['a', 'b', 'c', 2] 

2がまだそこにあります。正しい方法は次のようになります。

>>> foo = ['a', 'b', 'c', 1, 2, 3] 
>>> for item in foo[:]: 
...  if isinstance(item, int): 
...   foo.remove(item) 
... 
>>> foo 
['a', 'b', 'c'] 
+0

+1のためにpythonicであるために – neurino

+0

@ neurino:pythonicのアプローチは次のようになります: 'foo = [c for foo in condition(c)]' – jfs

+0

@ J.F。セバスチャン:正直言って、1ライナーに収まるものはすべて3行のコードよりももっとpyononicだとは思っていませんが、GreenMatが立ち往生している間にGilderが 'enumerate'を使っていたことを強調したいと思っていました。 ://stackoverflow.com/revisions/7423184/2))から 'x in xrange(len())'に変更します。 Cheers – neurino

13

PythonのJavaのではなく、またC/C++ - あなたは本当にパイソンのパワーを利用するそのように思考を停止する必要があります。

Pythonは値渡しも参照渡しもありませんが、代わりに名前渡し(またはパスバイオブジェクト)を使用します。つまり、ほぼすべてが名前にバインドされていますあなたはそれを使うことができます(2つの明らかな例外はタプルとリストの索引付けです)。

spam = "green"を実行すると、文字列オブジェクト"green"spamという名前がバインドされています。 eggs = spamあなたが何もコピーしていない場合は、参照ポインタを作成していません。 eggsという別の名前を同じオブジェクト(この場合は"green")にバインドしているだけです。 spamを別のもの(spam = 3.14159)にバインドすると、eggsは依然として"green"にバインドされます。

for-loopを実行すると、ループを実行している間にiterableの各オブジェクトに順番にバインドします。関数を呼び出すと、関数ヘッダーの名前が渡され、渡された引数にバインドされます。名前の再割り当ては実際に名前を再バインドしています(これを吸収するにはしばらく時間がかかります - とにかくそれは私のためでした)。

forループのリストを利用して、バックリストに割り当てるには2つの基本的な方法があります。

for i, item in enumerate(some_list): 
    some_list[i] = process(item) 

または

new_list = [] 
for item in some_list: 
    new_list.append(process(item)) 
some_list[:] = new_list 

は注意してくださいその最後some_list[:] - それが原因となっていますsome_listの要素の変異(new_listの要素に全体を設定)some_listnew_listに再バインドするのではなく、これは重要ですか?場合によります! some_list以外の他の名前が同じリストオブジェクトにバインドされていて、それらに更新を表示したい場合は、スライス方法を使用する必要があります。あなたがしない場合、またはあなたがでない場合、は、それらが更新を見たいと思って、その後rebind - some_list = new_list

+0

名前バインディングは、Javaと同じように動作します。私は何の違いも見られませんでした。 –

+1

intやbooleanなどのJavaプリミティブでは、値渡しされ、プリミティブではないすべてが参照渡しされます。名前へのバインドは、型の安全性を除いた参照渡しと似ていますが、おそらくいくつかの詳細があります。 javaでは、参照は、少なくともコンパイルに、Pythonの名前に参照できるものについてのデータを入力するような制限はありません。 – Sqeaky

関連する問題