编辑:让我尝试改写并改进我的问题。旧版本附在底部。
我正在寻找一种以类型通用的方式表达和使用自由函数的方法。例子:
abs(x) # maps to x.__abs__()
next(x) # maps to x.__next__() at least in Python 3
-x # maps to x.__neg__()
在这些情况下,函数的设计方式允许具有用户定义类型的用户通过将工作委托给非静态方法调用来自定义其行为。这很好。它允许我们编写并不真正关心确切参数类型的函数,只要它们“感觉”像模拟某个概念的对象即可。
反例: 不易通用的函数:
math.exp # only for reals
cmath.exp # takes complex numbers
假设,我想编写一个通用函数,将 exp 应用于类似数字的对象列表。我应该使用什么 exp 函数?我该如何选择正确的呢?
def listexp(lst):
return [math.exp(x) for x in lst]
显然,即使有复数的 exp(在 cmath 中),这也不适用于复数列表。而且它也不适用于任何用户定义的类似数字的类型,这些类型可能提供自己的特殊 exp 函数。
所以,我正在寻找一种从双方角度处理这个问题的方法——理想情况下不需要对很多东西进行特殊的处理。作为一些不关心参数的确切类型的通用函数的编写者,我想使用特定于所涉及类型的正确数学函数,而不必显式处理这个问题。作为用户定义类型的编写者,我想公开特殊的数学函数,这些函数已被增强以处理存储在这些对象中的附加数据(类似于复数的虚部)。
执行此操作的首选模式/协议/习惯用法是什么?我还没有测试numpy
。但我下载了它的源代码。据我所知,它为数组提供了一个 sin 函数。不幸的是,我还没有在源代码中找到它的实现。但看看他们如何为数组当前存储的正确类型的数字选择正确的 sin 函数将会很有趣。
在 C++ 中,我会依赖函数重载和 ADL(参数相关查找)。由于 C++ 是静态类型的,因此这(名称查找、重载解析)完全在编译时处理就不足为奇了。我想,我可以在运行时使用 Python 和 Python 提供的反射工具来模拟这一点。但我也知道尝试将一种编码风格导入另一种语言可能是一个坏主意,而且在新语言中也不是很惯用。因此,如果您对方法有不同的想法,我会洗耳恭听。
我想,在某个时候我需要以可扩展的方式手动执行一些依赖于类型的调度。也许编写一个模块“tgmath”(类型通用数学),它支持真实和复杂的支持,并允许其他人注册他们的类型和特殊情况函数......意见? Python大师们对此有何评论?
TIA
编辑:显然,我不是唯一对泛型函数和依赖于类型的重载感兴趣的人。有PEP 3124 http://www.python.org/dev/peps/pep-3124/但从4年前起它就处于草稿状态。
问题的旧版本:
我有很强的 Java 和 C++ 背景,最近才开始学习 Python。我想知道的是:我们如何扩展数学函数(至少是它们的名称)以便它们适用于其他用户定义的类型?这些类型的函数是否提供我可以利用的任何类型的扩展点/挂钩(类似于迭代器协议,其中next(obj)
实际上委托给obj.__next__
, etc) ?
在 C++ 中,我只需使用新的参数类型重载该函数,并让编译器找出哪些函数是使用参数表达式的静态类型表示的。但由于 Python 是一种非常动态的语言,因此不存在重载之类的事情。执行此操作的首选 Python 方法是什么?
另外,当我编写自定义函数时,我想避免长链
if isinstance(arg,someClass):
suchandsuch
elif ...
我可以使用哪些模式来使代码看起来更漂亮、更Pythonish?
我想,我基本上是在尝试解决 Python 中函数重载不足的问题。至少在 C++ 中,重载和参数相关查找是良好 C++ 风格的重要组成部分。
是否有可能使
x = udt(something) # object of user-defined type that represents a number
y = sin(x) # how do I make this invoke custom type-specific code for sin?
t = abs(x) # works because abs delegates to __abs__() which I defined.
工作?我知道我可以使 sin 成为该类的非静态方法。但随后我就失去了通用性,因为对于其他所有类型的类似数字的对象来说,它都是sin(x)
并不是x.sin()
.
添加一个__float__
方法是不可接受的,因为我在对象中保留了附加信息,例如“自动微分”的导数。
TIA
编辑:如果您对代码的样子感到好奇,请检查this http://ideone.com/H6z60出去。在理想的世界中,我将能够以类型通用的方式使用 sin/cos/sqrt。我认为这些函数是对象接口的一部分,即使它们是“自由函数”。在__somefunction
我没有限定这些功能math.
nor __main__.
。它之所以有效,是因为我手动依靠math.sin
(等等)通过装饰器在我的自定义函数中。但我认为这是一个丑陋的黑客行为。