views:

142

answers:

4

I have this class:

class STR(str):

    def __int__(self):
        return 42

If i use it in the promt like this:

>>> a=STR('8')
>>> ord(a)
56
>>> int(a)
42
>>> chr(a)
'*'

that's the behaivour. I'd like to ord(a) be 42. How can I do it? Which method should I override in the str class? Is all this documented anywhere?

Thanks!

+1  A: 

Here's the C source for Python's builtin ord from the current SVN revision of bltinmodule.c:

static PyObject *
builtin_ord(PyObject *self, PyObject* obj)
{
    long ord;
    Py_ssize_t size;

    if (PyString_Check(obj)) {
     size = PyString_GET_SIZE(obj);
     if (size == 1) {
      ord = (long)((unsigned char)*PyString_AS_STRING(obj));
      return PyInt_FromLong(ord);
     }
    } else if (PyByteArray_Check(obj)) {
     size = PyByteArray_GET_SIZE(obj);
     if (size == 1) {
      ord = (long)((unsigned char)*PyByteArray_AS_STRING(obj));
      return PyInt_FromLong(ord);
     }

#ifdef Py_USING_UNICODE
    } else if (PyUnicode_Check(obj)) {
     size = PyUnicode_GET_SIZE(obj);
     if (size == 1) {
      ord = (long)*PyUnicode_AS_UNICODE(obj);
      return PyInt_FromLong(ord);
     }
#endif
    } else {
     PyErr_Format(PyExc_TypeError,
           "ord() expected string of length 1, but " \
           "%.200s found", obj->ob_type->tp_name);
     return NULL;
    }

    PyErr_Format(PyExc_TypeError,
          "ord() expected a character, "
          "but string of length %zd found",
          size);
    return NULL;
}

As you can see, it doesn't make any method calls on the instance you pass in. As far as I can tell, if you don't pass it an explicit string, there's no way to override the functionality of ord.

In fact, it's essentially verifying that the PyObject is either a string, byte array, or Unicode -- that's what the PyString_Check, etc. functions do. If it's none of those, you get the TypeError exception.

One workaround that doesn't scale very well would be to write your own ord in the global namespace:

>>> class STR(str):
...     def __int__(self):
...             return 42
... 
>>> 
>>> def ord(s):
...     if isinstance(s, STR):
...             return int(s)
...     else:
...             return __builtins__.ord(s)
... 
>>>  
>>> ord(STR('fdsafds'))
42
>>> ord("!")
33

Of course, this scales horribly because another module using your class may be directly calling __builtins__.ord, or they might even be overwriting ord themselves! But, if you only need this to work on a single, standalone module, this is one way to go about it.

Mark Rushakoff
I like this answer. Which is the scale problem? I can't see it very well.
Juanjo Conti
A: 

ord is not overloadable, it doesn't call any method on string objects (at least in CPython - Python/bltinmodule.c, builtin_ord function).

The best way is probably to create STR.ord method and call that instead of the built-in.

PiotrLegnica
A: 

You can't override ord but you can overwrite as variable for example

backup original ord

origord= ord

def ord(x):
    return 42


ord(a)
42

origord(a)
56

But I think its not good a idea to overwrite it.

S.Mark
mmmmmm this gave me an idea.
Juanjo Conti
:D but you know, it will confuse yourself and others people who read that codes later.
S.Mark
A: 

Somebody already posted the builtin ord code, and there is no method call you may intercept.

One solution could be override the ord function, for example:

backup_ord = ord
def ord(obj):
    if hasattr(obj, '__ord__'):
        return obj.__ord__()
    else:
        return backup_ord(obj)

Then you define your class with the __ord__ method and do something like:

class MyStr(str):
    def __ord__(self):
        return 'LOL'

For tests:

normal_five = '5'
strange_five = MyStr('5')
print ord(normal_five)
print ord(strange_five)

Outputs:

53
LOL
Andrea Ambu