简体   繁体   English

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

[英]Overload all arithmetic operators in Python

Suppose I build a class that basically represents a number plus some fancy stuff. 假设我构建了一个基本上代表数字加上一些奇特东西的类。 Instances of that class should behave like numbers in any arithmetic/mathematical operation. 在任何算术/数学运算中,该类的实例应该像数字一样。

I could overload all numeric operators in that class, but is there no shorter solution? 我可以重载该类中的所有数字运算符,但是没有更短的解决方案吗?

The class basically looks like: 该课程基本上如下:

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

What about this? 那这个呢?

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'

I guess based on the above, you can figure out what you need. 我想根据上述情况,你可以弄清楚你需要什么。

I don't endorse this as being particularly idiomatic, but... 我并不认为这是特别惯用的,但......

Assuming all of your function definitions behave identically, like "just invoke the base behavior of the self.num class and apply all the non-self arguments to it", then you can loop through all the function names you want to define, and create each one using setattr . 假设所有函数定义的行为相同,例如“只调用self.num类的基本行为并将所有非自身参数应用于它”,那么您可以循环遍历要定义的所有函数名称,并创建每一个都使用setattr Example: 例:

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)

Result: 结果:

MyFancyNumber(60, 'hello')
MyFancyNumber(40, 'hello')
MyFancyNumber(500, 'hello')
MyFancyNumber(5, 'hello')
MyFancyNumber(-51, 'hello')
MyFancyNumber(-50, 'hello')
MyFancyNumber(50, 'hello')

Edit: I wasn't sure whether you wanted the result of arithmetic to be a MyFancyNumber or a regular built-in numerical type, but either way, the implementation is pretty similar: 编辑:我不确定你是否希望算术的结果是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)

Result: 结果:

60
40
500
5
-51
-50
50

No, you have to define all the arithmetic operators otherwise how would Python know what to do with them. 不,你必须定义所有算术运算符,否则Python将如何知道如何处理它们。 Don't forget you also need the reverse operators like __radd__ . 不要忘记你还需要像__radd__这样的反向运算符。

Also the code you've written returns an int for x+1 . 您编写的代码也返回x+1int Did you mean that or did you want adding a fancy number to return another fancy number? 你的意思是,或者你想要添加一个花哨的数字来返回另一个花哨的数字?

You could just subclass int or float . 你可以只是继承intfloat Then you don't have to reimplement the operators but you'll still lose the special nature whenever you operate on a value. 然后你不必重新实现运算符,但是每当你操作一个值时你仍然会失去特殊性质。

A better solution would just be to have the numeric value in an attribute and convert to numbers explicitly when that's what you want. 更好的解决方案就是在属性中使用数值,并在需要时明确转换为数字。 You can use __int__() and __float__() to implement the conversion. 您可以使用__int__()__float__()来实现转换。

The documentation covers what you need to do if you really do want to emulate a numeric type: for Python 3.x https://docs.python.org/3/reference/datamodel.html?highlight= int #emulating-numeric-types or for Python 2.x https://docs.python.org/2/reference/datamodel.html?highlight= int #emulating-numeric-types 如果您真的想模拟数字类型,那么文档将介绍您需要执行的操作:对于Python 3.x https://docs.python.org/3/reference/datamodel.html?highlight= int #muting-digital-类型或Python 2.x https://docs.python.org/2/reference/datamodel.html?highlight= int #emulating-numeric-types

This works for me in python 2.7 as long as you pass just one argument in the init . 只要你在init中传递一个参数,这对我来说就适用于python 2.7。 Sadly have no idea why it works though. 可悲的是,不知道为什么它有效。

class MyFancyNumber(int):
    def __init__(self, num):
        self.num = num # the actual number

    def add_info(self,info):
        self.info = info  ## Add the info separately

    def doFancyStuff(self):
        # does something fancy

print MyFancyNumber(5)+5    

Usage 用法

f = MyFancyNumber(2)
f.add_info(info)
f+4               ## returns 6

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM