6

私が取り組んでいるプロジェクトでは、リンクされたリストのデータ構造を実装しています。これは、次のように定義するペアのアイデアに基づいています。

class Pair:
    def __init__(self, name, prefs, score):
        self.name = name
        self.score = score
        self.preferences = prefs
        self.next_pair = 0
        self.prev_pair = 0

ここでself.next_pair、 とself.prev_pairは、それぞれ前のリンクと次のリンクへのポインターです。

リンクリストをセットアップするために、次のようなインストール機能があります。

def install(i, pair):
    flag = 0
    try:
        old_pair = pair_array[i]
        while old_pair.next_pair != 0:
            if old_pair == pair:
                #if pair in remainders: remainders.remove(pair)
                return 0
            if old_pair.score < pair.score:
                flag = 1
                if old_pair.prev_pair == 0: # we are at the beginning
                    old_pair.prev_pair = pair
                    pair.next_pair = old_pair
                    pair_array[i] = pair
                    break
                else: # we are not at the beginning
                    pair.prev_pair = old_pair.prev_pair
                    pair.next_pair = old_pair
                    old_pair.prev_pair = pair
                    pair.prev_pair.next_pair = pair
                    break
            else:
                old_pair = old_pair.next_pair
        if flag==0:
            if old_pair == pair:
                #if pair in remainders: remainders.remove(pair)
                return 0
            if old_pair.score < pair.score:
                if old_pair.prev_pair==0:
                    old_pair.prev_pair = pair
                    pair.next_pair = old_pair
                    pair_array[i] = pair
                else:
                    pair.prev_pair = old_pair.prev_pair
                    pair.next_pair = old_pair
                    old_pair.prev_pair = pair
                    pair.prev_pair.next_pair = pair
            else:
                old_pair.next_pair = pair
                pair.prev_pair = old_pair
        except KeyError:
            pair_array[i] = pair
            pair.prev_pair = 0
            pair.next_pair = 0

プログラムの過程で、私はこれらのリンクされたリストの辞書を作成し、いくつかのリンクを削除して別のリストに追加しています。プルーニングと再インストールの間、リンクは中間配列に格納されます。

このプログラムをデバッグしているうちに、Python が引数を関数に渡す方法についての私の理解に欠陥があることに気付きました。私が書いたこのテストケースを考えてみましょう:

def test_install():
    p = Pair(20000, [3, 1, 2, 50], 45)
    print p.next_pair
    print p.prev_pair
    parse_and_get(g)
    first_run()
    rat = len(juggler_array)/len(circuit_array)
    pref_size = get_pref_size()
    print pref_size
    print install(3, p)
    print p.next_pair.name
    print p.prev_pair             

このテストを実行すると、次の結果が得られます。

0
0
10
None
10108
0

私が理解していないのは、 の 2 番目の呼び出しが最初の呼び出し ( ) とp.next_pairは異なる結果 ( ) を生成する理由です。渡されたものを上書きできるオブジェクトを返しません( を返します) 。ポインターを渡しているようではありません。101080installPairNoneinstall

値渡しについての私の理解では、インタープリターは関数に渡された値をコピーし、呼び出し元の変数は変更されないままです。たとえば、私が言うなら

def foo(x):
     x = x+1
     return x

baz = 2
y = foo(baz)
print y
print baz

次に3、 と2をそれぞれ印刷する必要があります。実際、Python インタープリターでそれをテストすると、それが起こります。

ここで誰かが私を正しい方向に向けることができれば、本当に感謝しています。

4

4 に答える 4

8

Pythonは、変数を関数に渡すときに何もコピーしません。これは、値による呼び出しでも参照による呼び出しでもありませんが、これら2つのうち、参照による呼び出しに似ています。これは「値による呼び出しですが、値は参照です」と考えることができます。

可変オブジェクトを関数に渡す場合、関数内でそのオブジェクトを変更すると、オブジェクトが表示されるすべての場所に影響します。(文字列や整数などの不変オブジェクトを関数に渡す場合、定義上、オブジェクトを変更することはできません。)

これが技術的に参照渡しではない理由は、名前を再バインドして、名前が他の何かを完全に参照するようにすることができるためです。(不変オブジェクトの名前の場合、これが実行できる唯一のことです。)関数内にのみ存在する名前を再バインドしても、関数外に存在する可能性のある名前には影響しません。

オブジェクトを使用した最初の例では、Pairオブジェクトを変更しているため、関数の外部で効果を確認できます。

2番目の例では、オブジェクトを変更せず、名前を他のオブジェクト(この場合は他の整数)に再バインドするだけです。 bazは、値が2の整数オブジェクト(Pythonでは、すべてがオブジェクトであり、整数も含む)を指す名前です。に渡すbazfoo(x)、名前はスタック上xの関数内でローカルに作成され、ポインターに設定されます。それは関数に渡されました-と同じポインタ。しかし、同じものではなく、同じオブジェクトへのポインタのみが含まれています。この行では、は値3の整数オブジェクトを指すようにリバウンドされ、そのポインターは関数から返され、整数オブジェクトをyにバインドするために使用されます。fooxbazxbazx = x+1x

最初の例を書き直して、渡されたPairオブジェクトからの情報に基づいて、関数内に新しいPairオブジェクトを明示的に作成した場合(これがコピーであるかどうか、次に変更するか、構築時にデータを変更するコンストラクターを作成するかどうか)そうすれば、関数には、渡されたオブジェクトを変更するという副作用はありません。

編集:ちなみに、Pythonでは、0「値がありません」という意味のプレースホルダーとして使用しないでください。使用してNoneください。同様に、でやっているように見えるように、0を意味するために使用するべきではありません。ただし、すべて、およびブール式で評価されるため、どちらを使用しても、の代わりに次のように言うことができます。Falseflag0NoneFalseFalseif not flagif flag == 0

于 2012-05-22T01:35:17.330 に答える
8

Pythonでは、すべてがオブジェクトです。単純な割り当てでは、割り当てられたオブジェクトへの参照が割り当てられた名前に保存されます。その結果、Python変数は、名前付きの場所に格納されているオブジェクトではなく、オブジェクトに割り当てられている名前と考える方が簡単です。

例えば:

baz = 2

...他の場所に格納されているbaz整数オブジェクトへのポインタまたは参照に2格納されます。(型intは不変であるため、Pythonには実際には小さな整数のプールがあり、2どこでも同じオブジェクトを再利用しますが、これは実装の詳細であり、あまり気にする必要はありません。)

を呼び出すとfoo(baz)foo()のローカル変数xも最初は整数オブジェクトを指します2。つまり、foo()-localnamexとglobalnameはbaz、同じオブジェクトの名前です2。その後x = x + 1、実行されます。これはx、別のオブジェクトを指すように変更されます3

理解することが重要です:xはを保持するボックスではなく、2その後2にインクリメントされ3ます。いいえ、x最初はを指し、2次にそのポインタはを指すように変更され3ます。当然、オブジェクトが指すものを変更しなかったのでbaz、それでも。を指し2ます。

それを説明する別の方法は、Pythonでは、すべての引数の受け渡しは値によるものですが、すべての値はオブジェクトへの参照であるということです。

これの直感に反する結果は、オブジェクトが可変である場合、任意の参照を介して変更でき、すべての参照が変更を「認識」することです。たとえば、次のことを考慮してください。

baz = [1, 2, 3]

def foo(x):
   x[0] = x[0] + 1

foo(baz)
print baz
>>> [2, 2, 3]

これは、最初の例とは大きく異なるようです。しかし実際には、議論は同じように渡されます。名前の下foo()へのポインタを受け取り、それを変更する操作を実行します(この場合、リストの最初の要素は別のオブジェクトを指します)。違いは、名前が新しいオブジェクトを指すことは決してないということです。別のオブジェクトを指すように変更されます。それ自体はまだと同じオブジェクトを指しています。(実際、内部では、への割り当てはメソッド呼び出しになります。)したがって、リストへの変更を「認識」します。どうしてできなかったの?bazxintxx[0]xbazx[0]x.__setitem__()baz

整数または文字列は変更できないため、整数および文字列ではこの動作は見られません。それらは不変の型であり、それらを変更するとき(たとえばx = x + 1)、実際にはそれらを変更するのではなく、変数名を完全に異なるオブジェクトにバインドします。たとえば、bazタプルに変更すると、タプルの要素に割り当てることができないため、エラーが発生します。タプルは別の不変のタイプです。タプルを「変更」するには、新しいタプルを作成する必要があります。次に、割り当てによって変数が新しいオブジェクトを指すようになります。baz = (1, 2, 3)foo()

定義したクラスのオブジェクトは変更可能であるため、Pairインスタンスは渡される任意の関数によって変更できます。つまり、属性を他のオブジェクトに追加、削除、または再割り当てできます。これらのいずれも、オブジェクトを指している名前を再バインドしないため、現在オブジェクトを指しているすべての名前が変更を「認識」します。

于 2012-05-22T01:45:18.763 に答える
2

リンクリストの実装を忘れて、Pythonのインスタンスを使用することをお勧めしますlist。デフォルトのPython以外のものが必要な場合は、listなどのPythonモジュールから何かを使用できますcollections

リンクリスト内のリンクをたどるPythonループは、Pythonインタープリターの速度で、つまりゆっくりと実行されます。組み込みクラスを使用するだけの場合list、リスト操作はPythonのCコードで行われ、速度が向上します。

リストのようなものが必要であるが、高速挿入と高速削除が必要な場合、あなたはdict作品を作ることができますか?値に順序付けを課すために使用できる何らかのID値(文字列または整数など)がある場合は、それをキー値として使用して、値の挿入と削除を非常に高速に行うことができます。次に、値を順番に抽出する必要がある場合は、dict.keys()メソッド関数を使用してキー値のリストを取得し、それを使用できます。

ただし、リンクリストが本当に必要な場合は、他の誰かが作成およびデバッグしたコードを見つけて、ニーズに合わせて調整することをお勧めします。Googleで「pythonリンクリストレシピ」または「pythonリンクリストモジュール」を検索します。

于 2012-05-22T01:45:54.697 に答える
1

少し複雑な要素を投入します。

>>> def foo(x):
...   x *= 2
...   return x
... 

数値、リスト、および文字列でサポートされていることがわかっている方法を使用して、わずかに異なる関数を定義します。

まず、文字列で呼び出します。

>>> baz = "hello"
>>> y = foo(baz)
>>> y
'hellohello'
>>> baz
'hello'

次に、リストで呼び出します。

>>> baz=[1,2,2]
>>> y = foo(baz)
>>> y
[1, 2, 2, 1, 2, 2]
>>> baz
[1, 2, 2, 1, 2, 2]
>>> 

文字列の場合、引数は変更されません。リストを使用すると、引数が変更されます。

私だったら、メソッド内の引数を変更することは避けます。

于 2012-05-22T01:22:50.170 に答える