web-dev-qa-db-ja.com

pythonで値渡しの動作をエミュレートする

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

可能な方法の1つは、ディープコピーを使用することです。

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

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

編集

技術的にはpythonのすべてが値で渡されることを知っています。エミュレート動作に興味がありました。関数に渡されたデータを混乱させる最も一般的な方法は、問題のオブジェクトを独自のクローンメカニズムまたはディープコピーでクローンすることです。

56
Boris Gorelik

Pythonでこれを行う方法はありません。

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


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

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

[...]

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

したがって、組み込みのPythonタイプまたは独自のオブジェクト(___copy___/___deepcopy___特別なメソッド、独自のclone()メソッドを定義する必要はありません)。

31
dF.

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

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

これは最も堅牢な方法ではなく、キーと値の引数またはクラスメソッド(自己引数の欠如)を処理しませんが、全体像はわかります。

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

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

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

31
Skurmedel

たとえばlistのように、参照として機能する組み込みtypがいくつかあります。

したがって、この例では、リストの場合、値渡しを行うPythonの方法は次のようになります。

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

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

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

先ほど言ったように、組み込みの型は数個しかなく、その動作はこの例の参照、リストのようなものです。

いずれにせよ...それが役立つことを願っています。

8
user695800

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

3
Tom

他のPythonicオプションはわかりません。しかし、個人的に私はより多くを好む[〜#〜] oo [〜#〜]方法。

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

def f(data):
  #do stuff

def caller():
  d = TheData()
  f(d.clone())
2
Sake

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

import pickle

def f(data):
    data = pickle.loads(pickle.dumps((data)))
    #do stuff

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

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]
0
ejectamenta