【问题标题】:How to create a copy of a python function [duplicate]如何创建python函数的副本[重复]
【发布时间】:2012-11-10 06:48:31
【问题描述】:

是否有可能创建 Python 函数的真实副本?最明显的选择是http://docs.python.org/2/library/copy.html,但我读到了:

它确实“复制”函数和类(浅层和深层),通过 原样返回原对象;

我需要一个真实的副本,因为我可能会更改函数的某些属性。

更新:

我知道 cmets 中提到的所有可能性。我的用例基于元编程,我根据一些声明性规范构造类。完整的细节对于 SO 来说太长了,但基本上我有一个类似的功能

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

我会将这个方法添加到各种类中。这些类可以完全不相关。使用 mixin 类不是一种选择:我将拥有许多这样的函数,并且最终会为每个函数添加一个基类。我目前的“解决方法”是将这个函数包装在这样的“工厂”中:

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

这样我总能得到一个新的 do_something_useful 函数,但我必须像这样包装我的所有函数。

您可以相信我,我知道,这不是“正常”的 OO 编程。我知道如何“正常”地解决这样的问题。但这是一个动态代码生成器,我希望一切都尽可能轻量和简单。而且由于python函数是很普通的对象,我觉得问怎么复制也不会太奇怪!?

【问题讨论】:

  • 如果你真的必须使用copy.deepcopy。但我认为某处存在更深层次的设计缺陷。首先为什么要复制函数?
  • 您能帮我们了解一下您想要做什么——您的问题有点奇怪/含糊。在我编写的数千行python中,我从未复制过一个函数。
  • 如果你需要维护状态,为什么不使用类和实例呢?为什么函数需要以某种方式复制
  • 如果要更改函数的属性,最好将函数包装在对象中,然后更改对象的属性。它会让你的代码更干净。如果向对象添加__call__ 方法,则可以像函数一样调用它。
  • 我不明白为什么在 python 中像对待任何其他对象一样处理函数应该是一个问题。我确实有同样的需求:装饰函数被传递给期望函数对象的内部和外部代码。复制一个函数来改变它的装饰因此很有用,而使用看起来像函数但不是的对象只是过于复杂。

标签: python deep-copy


【解决方案1】:

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]

【讨论】:

  • 函数字典也要复制:g.__dict__.update(f.__dict__)
  • functools.update_wrapper 将更新除__kwdefaults__ 之外的所有元数据,例如__doc____module__
  • 目前,此答案基于 Glenn Maynard 对类似问题的回答。在同一个问题下,an updated answer by Aaron Hall 与此答案非常相似。 Aaron 在旧的基础上更新了新的 dict,而这个答案使用 update_wrapper 来单独更新属性。我想知道(在 Python 3 的情况下)结果是否总是相同的。
  • @BenMares:使用g = functools.update_wrapper(g, f),你得到g.__wrapped__,它等于f。如果您使用fn.__dict__.update(f.__dict__),则不会获得__wrapped__ 属性。由于update_wrapper,我不知道有任何其他差异。
  • @BenMares:inclusion of g.__kwdefaults__ = f.__kwdefaults__ 带来了更大的不同。这样,g(1,2,3) 返回(1, 2, 3, (), 'FOO', 'BAR', 'BAZ')。没有它,g(1,2,3) 会引发 TypeError: f() missing 3 required positional arguments: 'arg1', 'arg2', and 'arg3'
猜你喜欢
  • 2014-10-07
  • 1970-01-01
  • 1970-01-01
  • 2017-01-14
  • 2018-11-30
  • 2019-08-18
  • 2017-06-27
  • 2011-01-27
相关资源
最近更新 更多