Random integer behavior

After looking at this question , I began to wonder: is it possible to write a class that behaves like a random integer?

I managed to find some overridable methods with dir() :

 class RandomInt(int): def __add__(self, other): return randint(1, 100) + other def __mul__(self, other): return randint(1, 100) * other def __div__(self, other): return randint(1, 100) / other def __sub__(self, other): return randint(1, 100) - other def __repr__(self): return str(randint(1, 100)) 

But I feel that there is a more elegant way to insert randint(1, 100) into every method that takes a self argument.

Is there a way to do this without re-writing the entire int class from scratch?

Something like:

 >>> x = RandomInt() >>> x + 1 2 >>> x + 1 74 >>> x * 4 152 
+7
source share
4 answers

This is a different answer because it is very different from the other that I posted. (and I felt it deserved to be separate)

The code:

 class RandomInt: def __getattr__(self, name): attr = getattr(int, name, '') if attr != '': def wrapper(*args, **kw): return attr(random.randint(1, 100), *args, **kw) return wrapper else: raise AttributeError( "'{0}' object has no attribute '{1}'".format('RandomInt',name)) 

Example:

 >>> x = RandomInt() >>> x 88 >>> 1 + x # __radd__ 67 >>> x*100 # __mul__ 1900 >>> x+5 # __add__ 50 >>> x-1000 # __sub__ -945 >>> x//5 # __floordiv__ 8 >>> float(x) # __float__ 63.0 >>> str(x) # __str__ '75' >>> complex(x) # __complex__ (24+0j) >>> sum([x]*10) 573 

There are opportunities for improvement:

 >>> x + x Traceback (most recent call last): File "<pyshell#1456>", line 1, in <module> x + x TypeError: unsupported operand type(s) for +: 'instance' and 'instance' 

Same for x*x , x/x and similar


Another version this time, similar to @gatto :

 import random, inspect class RandomInt: def __init__(self): def inject(attr): def wrapper(*args, **kw): args = list(args) for i,x in enumerate(args): if isinstance(x, RandomInt): args[i] = x+0 return attr(random.randint(1,100), *args, **kw) return wrapper for name in dir(int): attr = getattr(int, name) if inspect.ismethoddescriptor(attr): setattr(self, name, inject(attr)) 

And it has support for:

 >>> x + x 49 >>> x // x 2 >>> x * x 4958 >>> x - x 77 >>> x ** x 467056167777397914441056671494001L >>> float(x) / float(x) 0.28 

Another version using class attributes to overcome the new style / old style problem (thanks @gatto):

 import random, inspect class RandomInt(object): pass def inject(attr): def wrapper(*args, **kw): args = list(args) for i,x in enumerate(args): if isinstance(x, RandomInt): args[i] = random.randint(1,100) return attr(*args, **kw) return wrapper for name in dir(int): attr = getattr(int, name) if inspect.ismethoddescriptor(attr): setattr(RandomInt, name, inject(attr)) 

Output:

 >>> x 86 >>> x 22 >>> x * x 5280 >>> [1] * x [1, 1, 1, 1, 1, 1] >>> x * '0123' '0123012301230123' >>> s[x] # s = '0123456789' * 10 '5' 
+2
source
 import inspect from random import randint class SelfInjecter(type): def __new__(self, *args, **kw): cls = type(*args, **kw) factory = cls.__factory__ def inject(attr): def wrapper(self, *args, **kw): return attr(factory(self), *args, **kw) return wrapper for name in dir(cls): attr = getattr(cls, name) if inspect.ismethoddescriptor(attr): setattr(cls, name, inject(attr)) return cls class RandomInt(int): __metaclass__ = SelfInjecter __factory__ = lambda self: randint(1, 100) x = RandomInt() print x + 3, x - 3, x * 3, repr(x) 

There are several problems in the above code.

As suggested by Schoolboy , the following does not work properly:

 >>> print x * x 0 

We need to convert all the arguments to our new RandomInt type, if possible:

 def factory(x): if isinstance(x, cls): return cls.__factory__(x) return x def inject(attr): def wrapper(*args, **kw): args = [factory(x) for x in args] kw = {k: factory(v) for k, v in kw} return attr(*args, **kw) return wrapper 

Also sequence multiplication and indexing do not work properly:

 >>> [1] * x, x * '123', '123'[x] ([], '', '1') 

This is because Python does not use __index__ for int -inherited types:

 class Int(int): def __index__(self): return 2 >>> x = Int(1) >>> '012'[x], '012'[x.__index__()] ('1', '2') 

Here is the code from the Python 2.7.4 implementation:

 /* Return a Python Int or Long from the object item Raise TypeError if the result is not an int-or-long or if the object cannot be interpreted as an index. */ PyObject * PyNumber_Index(PyObject *item) { PyObject *result = NULL; if (item == NULL) return null_error(); if (PyInt_Check(item) || PyLong_Check(item)) { Py_INCREF(item); return item; } if (PyIndex_Check(item)) { result = item->ob_type->tp_as_number->nb_index(item); if (result && !PyInt_Check(result) && !PyLong_Check(result)) { PyErr_Format(PyExc_TypeError, "__index__ returned non-(int,long) " \ "(type %.200s)", result->ob_type->tp_name); Py_DECREF(result); return NULL; } } 

As you can see, it first checks int and long , and then tries to call __index__ .

The solution is to inherit from object and the clone / wrap attributes from int , or I really like Schoolboys more , I think it can be fixed in a similar way.

+1
source

One idea would be to have a __call__ method that returns a random number.

 class RandomInt(int): def __call__(self): return random.randint(1, 100) def __add__(self, other): return self() + other def __mul__(self, other): return self() * other def __div__(self, other): return self() / other def __sub__(self, other): return self() - other def __repr__(self): return str(self()) 

Run example

 >>> x = RandomInt() >>> x * 3 81 >>> x + 3 56 >>> x - 4 68 >>> x / 4 2 
0
source

You can attach methods at runtime:

 def add_methods(*names): def the_decorator(cls): for name in names: def the_function(self, other): return cls(random.randint(0, 100)) setattr(cls, name, the_function) return cls return the_decorator @add_methods('__add__', '__mul__', '__sub__') class RandomInt(int): pass 

This allows you to choose which method should be executed randomly.

Note that you may be tempted to use things like __getattr__ or __getattribute__ to configure access to attributes and not set methods explicitly in the class, but this will not work with special methods, since their search does not go through access methods to attributes .

0
source

All Articles