72

Pythonで値渡しの動作をエミュレートしたいと思います。言い換えれば、私が書いた関数がユーザー提供のデータを変更しないことを絶対に確認したいと思います。

考えられる1つの方法は、ディープコピーを使用することです。

from copy import deepcopy
def f(data):
    data = deepcopy(data)
    #do stuff

この目標を達成するためのより効率的またはよりPython的な方法があり渡されるオブジェクトについて可能な限り少ない仮定を行います(.clone()メソッドなど)

編集

技術的には、Pythonのすべてが値によって渡されることを認識しています。私はその振る舞いをエミュレートすることに興味がありました。つまり、関数に渡されたデータを台無しにしないようにすることです。最も一般的な方法は、問題のオブジェクトを独自のクローンメカニズムまたはディープコピーを使用してクローンすることだと思います。

4

8 に答える 8

45

これを行うためのPythonの方法はありません。

Pythonは、プライベートデータや読み取り専用データなどを強制するための機能をほとんど提供していません。pythonicの哲学は、「私たちはすべて大人に同意している」というものです。この場合、これは「関数がデータを変更してはならない」が仕様の一部であるが、コードでは強制されないことを意味します。


データのコピーを作成する場合、取得できる最も近いものはソリューションです。しかしcopy.deepcopy、非効率的であることに加えて、次のような警告もあります。

ディープコピーはすべてをコピーするため、コピーが多すぎる可能性があります。たとえば、コピー間でも共有する必要がある管理データ構造などです。

[...]

このモジュールは、モジュール、メソッド、スタックトレース、スタックフレーム、ファイル、ソケット、ウィンドウ、配列、または同様のタイプのようなタイプをコピーしません。

したがって、組み込みのPythonタイプまたは独自のオブジェクトを扱っていることがわかっている場合にのみお勧めします(__copy__/__deepcopy__特殊メソッドを定義することでコピー動作をカスタマイズできる場合は、独自のメソッドを定義する必要はありませんclone())。

于 2009-05-10T17:16:08.470 に答える
37

デコレータを作成し、その中にクローン動作を配置できます。

>>> def passbyval(func):
def new(*args):
    cargs = [deepcopy(arg) for arg in args]
    return func(*cargs)
return new

>>> @passbyval
def myfunc(a):
    print a

>>> myfunc(20)
20

これは最も堅牢な方法ではなく、Key-Value引数またはクラスメソッド(自己引数の欠如)を処理しませんが、全体像を把握できます。

次のステートメントは等しいことに注意してください。

@somedecorator
def func1(): pass
# ... same as ...
def func2(): pass
func2 = somedecorator(func2)

デコレータにクローン作成を行うある種の機能を実行させることもできます。これにより、デコレータのユーザーがクローン作成戦略を決定できるようになります。その場合、デコレータはおそらく__call__オーバーライドされたクラスとして実装するのが最適です。

于 2009-05-10T11:57:47.867 に答える
16

listたとえば、参照として機能する組み込みのtypは2つだけです。

したがって、私にとって、値渡しを行うためのpythonicな方法は、この例では次のようになります。

list1 = [0,1,2,3,4]
list2 = list1[:]

list1[:]list1の新しいインスタンスを作成し、それを新しい変数に割り当てることができます。

たぶん、1つの引数を受け取ることができる関数を記述し、その型をチェックし、その結果に従って、渡された引数の新しいインスタンスを返すことができる組み込み操作を実行することができます。

前に述べたように、組み込み型はごくわずかであり、それらの動作は参照のようであり、この例ではリストされています。

とにかく...それが役立つことを願っています。

于 2012-03-18T22:32:10.410 に答える
4

通常、データを外部APIに渡す場合、データを不変オブジェクトとして渡すことで、データの整合性を保証できます。たとえば、データをタプルにラップします。これをコードで防止しようとした場合、これを変更することはできません。

于 2009-07-04T12:30:47.993 に答える
3

他のpythonicオプションを理解できません。しかし、個人的にはもっとOOの方法を好みます。

class TheData(object):
  def clone(self):  """return the cloned"""

def f(data):
  #do stuff

def caller():
  d = TheData()
  f(d.clone())
于 2009-05-10T11:16:43.767 に答える
1

これを行うための本当にpythonicな方法はないと確信していますが、このpickleモジュールは、ビジネスで価値として扱っているすべてのもののコピーを提供することを期待しています。

import pickle

def f(data):
    data = pickle.loads(pickle.dumps((data)))
    #do stuff
于 2009-07-04T21:11:20.150 に答える
1

user695800答えに加えて、[:]演算子で可能なリストの値を渡します

def listCopy(l):
    l[1] = 5
    for i in l:
        print i

と呼ばれる

In [12]: list1 = [1,2,3,4]

In [13]: listCopy(list1[:])
1
5
3
4

list1
Out[14]: [1, 2, 3, 4]
于 2015-01-23T11:25:36.847 に答える
0

多くの人が標準ライブラリのコピーを使用しています。私は自分のクラスで定義することを好み__copy__ます。__deepcopy__のメソッドにcopyはいくつかの問題がある可能性があります。

  1. 浅いコピーは、新しいオブジェクトを作成する代わりに、元のオブジェクトへの参照を保持します。
  2. ディープコピーは再帰的に実行され、デッドループが発生する場合があります。注意を怠ると、メモリが爆発する可能性があります。

これらの制御不能な動作を回避するには、上書き__copy__とを使用して独自の浅い/深いコピー方法を定義します__deepcopy__。そして、アレックスの答えは良い例を示しています。

于 2019-12-30T15:22:23.660 に答える