编辑:让我试着重新措辞和改善我的问题。旧版本附在底部。
我正在寻找一种以通用类型的方式表达和使用自由函数的方法。例如:
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函数? 如何选择正确的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大师对此有何看法?
谢谢!
编辑:显然,我不是唯一对通用函数和类型相关重载感兴趣的人。有PEP 3124,但自4年前起仍处于草案状态。
旧版本的问题:
我在Java和C++方面有很强的背景,最近才开始学习Python。 我想知道的是:我们如何扩展数学函数(至少它们的名称),使它们适用于其他用户定义的类型? 这些类型的函数是否提供任何可以利用的扩展点/钩子(类似于迭代器协议,其中next(obj)
实际上委托给obj.__next__
等)?
在C ++中,我只需使用新参数类型重载函数,并让编译器根据参数表达式的静态类型确定应该使用哪个函数。 但由于Python是一种非常动态的语言,因此不存在重载。 做这件事的首选Python方式是什么?
此外,在编写自定义函数时,我希望避免长链
if isinstance(arg,someClass):
suchandsuch
elif ...
有哪些模式可以让代码看起来更漂亮、更符合Python的风格?
我想,我基本上是在尝试解决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。在理想情况下,我希望能以类型通用的方式使用sin/cos/sqrt。我认为这些函数是对象接口的一部分,即使它们是“自由函数”。在__somefunction
中,我没有使用math.
或__main__.
限定函数。这只是因为我通过装饰器在自定义函数中手动回退到math.sin
(等等)。但我认为这是一个丑陋的hack。
sin(x)
可以尝试回退到一个方法:x.__sin__()
…叹气… - sellibitzesin()
不是内置函数,因此通用对象的数据模型支持__sin__
方法并没有太多意义。在大多数情况下,如果在对象上调用math.sin()
有意义,那么也应该能够调用float()
。 - Wooble<built-in function sin>
- sellibitzeNameError: name 'sin' is not defined
。 - Wooble