''' The int methods of this Roman class are overloaded to return new Roman instances, so you can use it to perform wrap ints and perform basic math, like this: >>> x = Roman(3) >>> y = Roman(30) >>> print x, y Roman(3, III) Roman(30, XXX) >>> print x + y Roman(33, XXXIII) >>> print x * y Roman(90, LXXXX) >>> print y / x Roman(10, X) >>> print Roman(2) ** 3 Roman(8, VIII) >>> print Roman(25) % 11 Roman(3, III) >>> print Roman(-45) Roman(-45, -DCCCCLV) >>> sum(map(Roman, range(10))) Roman(45) >>> reduce(operator.mul, map(Roman, range(1, 5))) Roman(24) >>> print sum(map(Roman, range(10))) Roman(45, XXXXV) >>> print reduce(operator.mul, map(Roman, range(1, 5))) Roman(24, XXIV) ''' __author__ = "Thom" __copyright__ = None __license__ = "Python" import re def returnthisclassfrom(methods): ''' Class decorator by Alex Martelli. see ('http://stackoverflow.com/questions/1242589/ subclassing-int-to-attain-a-hex-representation/1243045#1243045) ''' def wrapit(cls, method): return lambda *a: cls(method(*a)) def dowrap(cls): for n in methods: method = getattr(cls, n) setattr(cls, n, wrapit(cls, method)) return cls return dowrap methods = "mul add sub div pow mod divmod".split() methods += [c + s for s in methods for c in list('ir')] methods = ['__%s__' % s for s in methods] methods = set(methods) & set(dir(int)) @returnthisclassfrom(methods) class Roman(int): i_ones = range(10) r_ones = ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'] ones_i2r = dict(zip(i_ones, r_ones)) i_mul = [10, 50, 100, 500, 1000] r_mul = ["X", "L", "C", "D", "M"] mul_i2r = dict(zip(i_mul, r_mul)) i = i_ones + i_mul r = r_ones + r_mul rom2int = dict(zip(r, i)) re_rom = re.compile('|'.join(sorted(r, key=len, reverse=True))) def as_roman(self): sign = "-" if self < 0 else "" res = [sign] n = int(self) mul_i2r = self.mul_i2r for x in reversed(self.i_mul): div, mod = divmod(n, x) n = mod res.append(mul_i2r[x] * div) res.append(self.ones_i2r[n]) return "".join(res) def __str__(self): return "Roman(%d, %s)" % (self, self.as_roman()) def __repr__(self): return "Roman(%d)" % self @classmethod def fromstring(cls, s): return cls(sum(map(cls.rom2int.get, cls.re_rom.findall(s)))) if __name__ == "__main__": for x in range(1, 1001): r = Roman(x) assert r == Roman.fromstring(r.as_roman()) print x, r print '\n' import operator x, y = Roman(20), Roman(5) print 'x =', x print 'y = ', y for s in "add sub div mul".split(): print '%s(x, y) = %s' % (s, getattr(operator, s)(x, y)) print '\n' x -= 1 print 'x % y = ', x % y