Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to overwrite str's % behaviour using __rmod__?

I'd like to do:

x %doSomething% y

which is easy enough to do (see code below) for any x and any y except in the case that x is a str.

Is there any way (e.g. adding a special method or raising a specific error) to cause old style string formatting to fail (similarly to how 1 %doSomthing fails with a TypeError) and revert to the __rmod__ method defined in the doSomething object?

class BinaryMessage(object):
  def __init__(self, fn):
    self._fn = fn
  def __rmod__(self, LHS):
    return BinaryMessagePartial(self._fn, LHS)

class BinaryMessagePartial(object):
  def __init__(self, fn, LHS):
    self._fn = fn
    self._LHS = LHS
  def __mod__(self, RHS):
    return self._fn(self._LHS, RHS)

def _doSomething(a , b):
  return a + b

doSomething = BinaryMessage(_doSomething)

result = 5 %doSomething% 6
assert result == 11
like image 766
DangerMouse Avatar asked Nov 03 '16 13:11

DangerMouse


1 Answers

Note: I submitted patches for Python 2.7, and 3.5 and up. These have landed and are part of 2.7.14, 3.5.4, 3.6.1 and 3.7, where the OP example now works as expected. For older versions, see below.


Unfortunately, this is not currently possible in Python. The behaviour is hardcoded in the evaluation loop:

TARGET(BINARY_MODULO) {
    PyObject *divisor = POP();
    PyObject *dividend = TOP();
    PyObject *res = PyUnicode_CheckExact(dividend) ?
        PyUnicode_Format(dividend, divisor) :
        PyNumber_Remainder(dividend, divisor);

(From the Python 3.5 source code, where PyUnicode is the Python str type).

This is unfortunate, because for every other type you can prevent the LHS.__mod__ method to be invoked by using a subclass for the right-hand operand; from the documentation:

Note: If the right operand’s type is a subclass of the left operand’s type and that subclass provides the reflected method for the operation, this method will be called before the left operand’s non-reflected method. This behavior allows subclasses to override their ancestors’ operations.

This would have been the only option here, str % other never returns NotImplemented, all RHS types are accepted (the actual str.__mod__ method only accepts str objects for the RHS, but is not called in this case).

I consider this a bug in Python, filed as issue #28598.

like image 80
Martijn Pieters Avatar answered Oct 23 '22 09:10

Martijn Pieters