在Python中重载所有算术运算符

Mic*_*ael 11 python

假设我构建了一个基本上代表数字加上一些奇特东西的类.在任何算术/数学运算中,该类的实例应该像数字一样.

我可以重载该类中的所有数字运算符,但是没有更短的解决方案吗?

该课程基本上如下:

class MyFancyNumber:
    def __init__(self, num, info):
        self.num = num # the actual number
        self.info = info # some more info, or other data
    def doFancyStuff(self):
        # does something fancy
    def __add__(self, other):
        return self.num + other # same pattern for all numeric functions
Run Code Online (Sandbox Code Playgroud)

Sza*_*lcs 8

那这个呢?

class MyFancyNumber(int):
    def __new__(cls, num, info=None):
        return super(MyFancyNumber, cls).__new__(cls, num)
    def __init__(self, num, info=None):
        self.num = num
        self.info = info
>>> MyFancyNumber(5)
5
>>> MyFancyNumber(5) + 2
7
>>> MyFancyNumber(5) / 4
1
>>> MyFancyNumber(5) * 0.5
2.5
>>> MyFancyNumber(5) - 7
-2
>>> MyFancyNumber(5, 'info').info
'info'
Run Code Online (Sandbox Code Playgroud)

我想根据上述情况,你可以弄清楚你需要什么.


Kev*_*vin 5

我不认为这是特别惯用的,但是......

假设您的所有函数定义的行为都相同,例如“只调用类的基本行为self.num并将所有非自参数应用于它”,那么您可以遍历要定义的所有函数名称,并使用创建每个函数名称setattr. 例子:

class MyFancyNumber(object):
    def __init__(self, num, info):
        self.num = num
        self.info = info
    def __repr__(self):
        return "MyFancyNumber({}, {})".format(repr(self.num), repr(self.info))

def make_func(name):
    return lambda self, *args: MyFancyNumber(getattr(self.num, name)(*args), self.info)

for name in ["__add__", "__sub__", "__mul__", "__div__", "__invert__", "__neg__", "__pos__"]:
    setattr(MyFancyNumber, name, make_func(name))

x = MyFancyNumber(50, "hello")
print(x + 10)
print(x - 10)
print(x * 10)
print(x / 10)
print(~x)
print(-x)
print(+x)
Run Code Online (Sandbox Code Playgroud)

结果:

MyFancyNumber(60, 'hello')
MyFancyNumber(40, 'hello')
MyFancyNumber(500, 'hello')
MyFancyNumber(5, 'hello')
MyFancyNumber(-51, 'hello')
MyFancyNumber(-50, 'hello')
MyFancyNumber(50, 'hello')
Run Code Online (Sandbox Code Playgroud)

编辑:我不确定您希望算术结果是 MyFancyNumber 还是常规的内置数字类型,但无论哪种方式,实现都非常相似:

class MyFancyNumber(object):
    def __init__(self, num, info):
        self.num = num
        self.info = info
    def __repr__(self):
        return "MyFancyNumber({}, {})".format(repr(self.num), repr(self.info))

def make_func(name):
    return lambda self, *args: getattr(self.num, name)(*args)

for name in ["__add__", "__sub__", "__mul__", "__div__", "__invert__", "__neg__", "__pos__"]:
    setattr(MyFancyNumber, name, make_func(name))

x = MyFancyNumber(50, "hello")
print(x + 10)
print(x - 10)
print(x * 10)
print(x / 10)
print(~x)
print(-x)
print(+x)
Run Code Online (Sandbox Code Playgroud)

结果:

60
40
500
5
-51
-50
50
Run Code Online (Sandbox Code Playgroud)


Dun*_*can 2

不,你必须定义所有算术运算符,否则 Python 如何知道如何处理它们。不要忘记您还需要反向运算符,例如__radd__.

您编写的代码也会返回一个intfor x+1。您是这个意思还是想添加一个奇特的数字以返回另一个奇特的数字?

你可以只是子类化intfloat. 这样您就不必重新实现运算符,但每当您对值进行操作时,您仍然会失去特殊性。

更好的解决方案是将数值放在属性中,并在需要时显式转换为数字。您可以使用__int__()__float__()来实现转换。

该文档涵盖了如果您确实想要模拟数字类型,则需要执行的操作:对于 Python 3.x https://docs.python.org/3/reference/datamodel.html?highlight= int #emulated-numeric-类型或 Python 2.x https://docs.python.org/2/reference/datamodel.html?highlight= int #emulated-numeric-types