在python中何时使用运算符重载的经验法则

ola*_*ndo 13 python operator-overloading

从我从我的C++类记得,教授说,操作符重载是很酷,但因为它需要比较大量的思想和代码覆盖所有终端的情况下(如过载时,+你可能还需要过载+++=,也使确保处理最终情况,例如向对象添加对象等.),您应该只考虑在此功能会对您的代码产生重大影响的情况下,例如在数学应用程序中重载矩阵类的运算符.

这同样适用于python吗?你会建议在python中覆盖运算符行为吗?你能给我什么经验法则?

Ale*_*lli 25

当您创建一个属于现有"抽象基类"(ABC)的新类时,运算符重载非常有用 - 实际上,标准库模块集合中的许多ABC 依赖于某些特殊方法的存在(和特殊方法)方法,一个名称以双下划线开头和结尾的方式AKA"dunders",正是你在Python中执行运算符重载的方式).这提供了良好的起始指导.

例如,一个Container必须覆盖特殊方法__contains__,即成员资格检查操作符item in container(如,if item in container:- 不要与for语句混淆for item in container:,依赖于__iter__! - ).同样,Hashable必须覆盖__hash__,Sized必须覆盖__len__,a SequenceMapping必须覆盖__getitem__,等等.(此外,基本知识可以提供您类的mixin功能-例如,二者SequenceMapping能提供__contains__您提供的基础上__getitem__重写,从而自动让你的类Container).

除此之外collections,如果您的新类"是一个数字",您将希望覆盖特殊方法(即提供运算符重载).其它特殊情况存在,但抵制运算符重载"只是冷静",具有"正常"的含义没有语义关联的诱惑,因为C++的流为做<<>>和Python字符串(Python中2.*,所幸没有3.*任何更多的;-)做%- 当这些操作员不再意味着"位移"或"除法余数"时,你只会产生混乱.一个语言的标准库可以随意使用它(虽然它不应该;-),但除非你的库像语言的标准库一样广泛,否则混乱会受到伤害! - )

  • BTW,对于那些因为没有%字符串格式而感到绝望的人:尽管Python 3文档描述%已经过时,但它仍然有文档记录,根据最近的讨论,该功能似乎不会真正消失,直到Python 4在python-dev中.这留下了充足的时间来学习和喜欢2.6中已有的新字符串格式方法. (4认同)

Pet*_*ter 12

我写了大量超载的软件,最近我对这个政策感到遗憾.我会这样说:

只有重载操作符,如果它是自然的,预期的事情,并没有任何副作用.

因此,如果你创建一个新RomanNumeral类,重载加法和减法等是有意义的.但是不要重载它,除非它是自然的:为一个Car或一个Vehicle对象定义加法和减法是没有意义的.

另一个经验法则:不要超负荷==.如果两个对象是相同的,实际测试它会非常困难(尽管不是不可能).我犯了这个错误并付了很长时间.

至于何时过载+=,++等等,其实我说:只有额外的超负荷运营商,如果你有很多关于这个功能的需求.有一种方法比五种方法更容易.当然,这意味着有时你必须写x = x + 1而不是x += 1,但如果它更清楚,更多的代码是可以的.

一般来说,就像许多"花哨"的功能一样,很容易认为你想要的东西,当你没有真正的,实现一堆东西,没有注意到副作用,然后后来弄清楚.Err在保守的一面.

编辑:我想添加一个关于重载的解释性说明==,因为似乎各种评论者误解了这一点,并且它让我感到困惑.是的,is存在,但这是一个不同的操作.假设我有一个对象x,它来自我的自定义类,或者是一个整数.我想看看数字是否x为500.但是如果你设置x = 500,那么稍后测试x is 500,你会得到False,因为Python缓存数字的方式.随着50,它会回来True.但你不能使用is,因为你可能要x == 500返回True,如果x是你的类的实例.混乱?当然.但这是成功超载运营商需要了解的细节.

  • 重载`==`/`__eq__`并没有错,实际上它可能是最重载的方法之一.Python有`is`来检查对象身份. (11认同)
  • 重载`++`并不特别适用,因为Python没有`++`运算符. (2认同)
  • 如果a是b:...`,即使`=='超载,你也不能测试两个对象是否相同?或者我误解了你的观点? (2认同)

Joh*_*ooy 5

下面是一个使用按位或操作来模拟unix管道的示例.这是大多数经验法则的反例.

我刚刚发现Lumberjack在实际代码中使用了这种语法



class pipely(object):
    def __init__(self, *args, **kw):
        self._args = args
        self.__dict__.update(kw)

    def __ror__(self, other):
        return ( self.map(x) for x in other if self.filter(x) )

    def map(self, x):
        return x

    def filter(self, x):
        return True

class sieve(pipely):
    def filter(self, x):
        n = self._args[0]
        return x==n or x%n

class strify(pipely):
    def map(self, x):
        return str(x)

class startswith(pipely):
    def filter(self, x):
        n=str(self._args[0])
        if x.startswith(n):
            return x

print"*"*80
for i in xrange(2,100) | sieve(2) | sieve(3) | sieve(5) | sieve(7) | strify() | startswith(5):
    print i

print"*"*80
for i in xrange(2,100) | sieve(2) | sieve(3) | sieve(5) | sieve(7) | pipely(map=str) | startswith(5):
    print i

print"*"*80
for i in xrange(2,100) | sieve(2) | sieve(3) | sieve(5) | sieve(7) | pipely(map=str) | pipely(filter=lambda x: x.startswith('5')):
    print i

Run Code Online (Sandbox Code Playgroud)

  • :)我管理员我没有在实际代码中使用它,但它是将生成器链接在一起的一种方便的方法.你可以做一些与惯例相似的东西,但语法变得更像`筛(2,筛(3,筛(5,筛(7))))`我不喜欢 (2认同)