web-dev-qa-db-ja.com

python関数のコピーを作成する方法

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関数を取得できますが、すべての関数をこのようにラップする必要があります。

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

21
Achim

Python

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]
31
unutbu