31

Python関数の実際のコピーを作成する可能性はありますか?最も明白な選択はhttp://docs.python.org/2/library/copy.htmlでしたが、そこで私は次のように読みました。

元のオブジェクトを変更せずに返すことにより、関数とクラスを(浅く深く)「コピー」します。

関数の一部の属性を変更する可能性があるため、実際のコピーが必要です。

アップデート:

コメントに記載されているすべての可能性を認識しています。私のユースケースは、いくつかの宣言型仕様からクラスを構築するメタプログラミングに基づいています。完全な詳細はSOには長すぎますが、基本的に私は次のような機能を持っています

def do_something_usefull(self,arg):
    self.do_work()

このメソッドをさまざまなクラスに追加します。それらのクラスは完全に無関係である可能性があります。ミックスインクラスを使用することはオプションではありません。そのような関数がたくさんあり、関数ごとに基本クラスを追加することになります。私の現在の「回避策」は、この関数を次のような「ファクトリ」でラップすることです。

def create_do_something():
    def do_something_usefull(self,arg):
        self.do_work()

そうすれば、常に新しいdo_something_useful関数を取得できますが、すべての関数をこのようにラップする必要があります。

これは「通常の」オブジェクト指向プログラミングではないことを私は知っていますが、あなたは私を信頼することができます。私はそのようなことを「普通に」解決する方法を知っています。しかし、これは動的なコードジェネレーターであり、すべてを可能な限り軽量かつシンプルに保ちたいと思います。そして、Python関数はごく普通のオブジェクトなので、それらをコピーする方法を尋ねるのはそれほど奇妙ではないと思います!?

4

1 に答える 1

45

Python3の場合:

import types
import functools

def copy_func(f):
    """Based on http://stackoverflow.com/a/6528148/190597 (Glenn Maynard)"""
    g = types.FunctionType(f.__code__, f.__globals__, name=f.__name__,
                           argdefs=f.__defaults__,
                           closure=f.__closure__)
    g = functools.update_wrapper(g, f)
    g.__kwdefaults__ = f.__kwdefaults__
    return g

def f(arg1, arg2, arg3, kwarg1="FOO", *args, kwarg2="BAR", kwarg3="BAZ"):
    return (arg1, arg2, arg3, args, kwarg1, kwarg2, kwarg3)
f.cache = [1,2,3]
g = copy_func(f)

print(f(1,2,3,4,5))
print(g(1,2,3,4,5))
print(g.cache)
assert f is not g

収量

(1, 2, 3, (5,), 4, 'BAR', 'BAZ')
(1, 2, 3, (5,), 4, 'BAR', 'BAZ')
[1, 2, 3]

Python2の場合:

import types
import functools
def copy_func(f):
    """Based on http://stackoverflow.com/a/6528148/190597 (Glenn Maynard)"""
    g = types.FunctionType(f.func_code, f.func_globals, name=f.func_name,
                           argdefs=f.func_defaults,
                           closure=f.func_closure)
    g = functools.update_wrapper(g, f)
    return g

def f(x, y=2):
    return x,y
f.cache = [1,2,3]
g = copy_func(f)

print(f(1))
print(g(1))
print(g.cache)
assert f is not g

収量

(1, 2)
(1, 2)
[1, 2, 3]
于 2012-11-21T22:43:29.007 に答える