如何动态创建与另一个函数相同签名的函数?

7 投票
3 回答
2230 浏览
提问于 2025-04-16 02:45

我正在忙着创建一个元类,这个元类的作用是把一个类中的一个占位函数替换成一个有实际实现的新函数。原来的函数可以有任何参数格式。我的问题是,我不知道怎么创建一个新函数,让它的参数格式和旧函数一样。该怎么做呢?

更新

这和我实际想问的问题没有关系,那个问题是 "我怎么动态创建一个和另一个函数参数格式一样的函数?",但我加上这个是为了说明我为什么不能使用子类。

我想在Python中实现类似于 Scala的案例类 的功能。(不是说模式匹配的部分,只是想要自动生成的属性、相等比较、哈希和字符串方法。)

我想要的效果大概是这样的:

>>> class MyCaseClass():
...     __metaclass__ = CaseMetaClass
...     def __init__(self, a, b):
...         pass

>>> instance = MyCaseClass(1, 'x')
>>> instance.a
1
>>> instance.b
'x'
>>> str(instance)
MyCaseClass(1, 'x')

据我所知,使用子类是无法做到这一点的。

3 个回答

-1

使用 functools.wraps

>>> from functools import wraps
>>> def f(a,b):
    return a+b
>>> @wraps(f)
def f2(*args):
    print(args)
    return f(*args)
>>> f2(2,5)
(2, 5)
7
0

其实是可以做到这一点的,使用inspect.getargspecs这个工具。甚至还有一个PEP文档来让这个过程更简单。

但是——这样做并不是个好主意。你能想象一下,如果你的函数是在运行时动态创建的,那会有多麻烦吗?而且还是通过一个元类来实现的!我不明白为什么你必须动态替换这个占位符;难道你不能在想要更改函数时直接修改代码吗?假设你有一个类

class Spam( object ):
    def ham( self, a, b ):
        return NotImplemented

由于你不知道这个类的具体功能,元类实际上无法实现任何功能。如果你知道ham应该做什么,你可以在ham或者它的父类中实现,而不是返回NotImplemented

3

我认为 functools.wraps 并不能完全保留原始函数的调用方式。不过,Michele Simionato 的 decorator模块 可以做到这一点:

import decorator

class FooType(type):
    def __init__(cls,name,bases,clsdict):
        @decorator.decorator        
        def modify_stub(func, *args,**kw):
            return func(*args,**kw)+' + new'
        setattr(cls,'stub',modify_stub(clsdict['stub']))
class Foo(object):
    __metaclass__=FooType
    def stub(self,a,b,c):
        return 'original'


foo=Foo()
help(foo.stub)
# Help on method stub in module __main__:

# stub(self, a, b, c) method of __main__.Foo instance

print(foo.stub(1,2,3))
# original + new

撰写回答