【问题标题】:Python operator overloading and operator associativityPython 运算符重载和运算符关联性
【发布时间】:2012-05-08 00:36:47
【问题描述】:

我正在编写一个 python 程序,我需要在其中重载 >> 运算符。我面临的问题是这个运算符需要是右关联的。因此,如果我执行以下操作

A >> B >> C >> D >> E

我希望将其解析为

(A >> (B >> (C >> (D >> E))))

据我了解,在 python 中,这个运算符是左关联的,所以我会得到,

((((A >> B) >> C) >> D) >> E)

在python中进行运算符重载时是否可以更改运算符的默认关联性?

【问题讨论】:

  • 我可能错了,但这不是运算符重载的问题,而是标准的“操作顺序”吗?如果它们都是相同的运算符,那么它将从左到右评估
  • 重载
  • @wim 我想到了,哈哈。只是对于习惯于从左到右阅读的人来说,以另一种方式阅读会更自然。我还考虑过重载 = 运算符,因为我认为这是正确的关联,但这会非常难看。
  • 这个的用例是什么?重载标准运算符来做神奇的事情几乎总是一个坏主意,因为它使其他人(或您在六个月内)难以弄清楚和维护代码。考虑def func(*args): ...func(A, B, C, D, E)
  • 很公平。尽管只使用一个命名良好的 KISS 函数可能仍然会更好。 Python 本身并不适合在 Python 中创建自定义 DSL,因为语法很难扩展。我相信你可以abstract syntax tree,但它很乱。

标签: python operator-overloading


【解决方案1】:

这可以做到……但需要一些努力。基本思想是使用右移运算符来创建和更新延迟实际计算的新型对象。

例如,假设您上面的变量:A、B、C、D 和 E 都是 Actual 类型的对象。我们将介绍一个新类 Deferred,它由对 Actual 实例的 rshift 操作生成。 Deferred 还实现了 rshift 运算符,它更新对象并返回自身。

(顺便说一句,对于这个答案的其余部分,我假设 A、B、C、D 和 E 是不可变的,并且 rshift 操作会产生一个新对象。)

F = A >> B >> C >> D >> E

将被计算为 ...

F = Deferred(A,B) >> C >> D >> E
F = Deferred(A,B,C) >> D >> E
F = Deferred(A,B,C,D) >> E
F = Deferred(A,B,C,D,E)

F 维护一个缓存的 Actual 实例,该实例是从反向序列计算的。此外,F 实现了与 Actual 相同的接口,因此在 Deferred 实例上调用的方法被委托给缓存的 Actual 实例。

我不知道你在做什么样的计算,所以在下面的例子中,我做了一些琐碎的事情,只是为了证明当延迟计算实际执行时,它们是相反的。

class Var(object):
    def __init__(self):
        pass

    @property
    def name(self):
        return self._name( )

    @property
    def length(self):
        return len(self.name)


class Actual(Var):
    def __init__(self, name):
        Var.__init__(self)
        self._text = name

    def _name(self):
        return self._text

    def __rshift__(self, other):
        if isinstance(other, Actual):
            return Deferred(self, other)

        return len(self.name)

    @staticmethod
    def NewFromShiftComputation(sequence):
        x = ' >> '.join(reversed(map(lambda actual: actual.name, sequence)))
        return Actual(x)



class Deferred(Var):
    def __init__(self, *args):
        Var.__init__(self)

        self._items  = [ ]
        self._actual = None  #-- cached "actual"

        for item in args:
            self._items.append(item)

    def _name(self):
        self._assure_actual( )
        return self._actual.name

    def __rshift__(self, other):
        self._actual = None  #-- Invalidate the cached "actual"
        self._items.append(other)
        return self

    def _assure_actual(self):
        if self._actual is None:
            self._actual = Actual.NewFromShiftComputation(self._items)



A = Actual('A')
B = Actual('B')
C = Actual('C')
D = Actual('D')
E = Actual('E')

F = A >> B >> C >> D >> E

print F.name
print F.length

【讨论】:

    【解决方案2】:

    这不是运营商的问题。 Python 从左到右求值:
    http://docs.python.org/reference/expressions.html#evaluation-order

    因此,在这种情况下需要括号,因为它都是相同的运算符。

    【讨论】:

    • 我害怕那个。我只是希望有某种方法可以告诉 python 解释器在某些情况下改变运算符的关联性。
    • @martega:是的,如果您可以更改 interp 的规则,这可能会破坏您正在使用的其他库。为什么不直接创建一个函数,将它们作为参数并运行正确的操作顺序?
    猜你喜欢
    • 2021-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-26
    • 2016-02-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多