I'm fairly new to Python (this is the first thing I've done that really takes advantage of classes). This works and I'm happy with it; just looking for general advice. Especially nice would be input on my comments and how 'pythonic' the code is.#import fractions #unused, on todoclass Base:  # Dicts for getting a digit from a value and a value from a digit  nums    = {0: '0', 1: '1', 2: '2', 3: '3', 4: '4', 5: '5', 6: '6', 7: '7', 8: '8', 9: '9', 10: 'A', 11: 'B', 12: 'C', 13: 'D', 14: 'E', 15: 'F', 16: 'G', 17: 'H', 18: 'I', 19: 'J', 20: 'K', 21: 'L', 22: 'M', 23: 'N', 24: 'O', 25: 'P', 26: 'Q', 27: 'R', 28: 'S', 29: 'T', 30: 'U', 31: 'V', 32: 'W', 33: 'X', 34: 'Y', 35: 'Z'}  rnums   = {'6': 6, 'Y': 34, '3': 3, 'M': 22, 'C': 12, '0': 0, '7': 7, 'X': 33, '1': 1, 'D': 13, 'W': 32, '9': 9, 'V': 31, 'U': 30, 'A': 10, 'L': 21, 'F': 15, 'O': 24, '4': 4, 'J': 19, 'Z': 35, 'I': 18, '5': 5, 'T': 29, 'P': 25, '2': 2, 'E': 14, 'R': 27, 'H': 17, 'S': 28, 'N': 23, '8': 8, 'B': 11, 'Q': 26, 'K': 20, 'G': 16}  # The special bases (binary, octal, hex) have different identifiers from the pattern followed by most  sbases  = {2: 'b', 8: 'o', 16: 'x'}  rsbases = {'b': 2, 'o': 8, 'x': 16}  def __init__(self, cols, base = 8, bigval = None): #cols is just a holdover; not sure what the best name for this general value would be    self.vals = {} # {column: value} dictionary    self.base = base    #can be created from an int & base, a str, a dict, or another Base object    if isinstance(cols, int) and bigval is None:      self.int_init(cols)    elif isinstance(cols, str):      if cols[0] == '0' and cols[2] == 'b': #valid string        self.baseNum_init(cols)      else:        raise ValueError('str init can only be used with a string of the form `0<base_char>b<basenum>`')        #self.vals = convert(int(cols)).vals         # ^Thought about this before but it shouldn't be allowed    elif isinstance(cols, dict):      self.dict_init(cols)    elif isinstance(cols, Base):      self.int_init(cols.base_ten())    else: #for using Base in a function like convert()      self.cols = cols      self.vals = {cols: bigval}  def int_init(self, num):    # The powers of the base, in a 1000-tuple of 2-tuples. i+1 b/c base**0 is the 1st column in a number.    self.powers = tuple( (i + 1, self.base**i) for i in range(1000 + 1) )    if self.base > len(nums) - 1 or self.base < 2: #unacceptable bases: <2 or more than are in the dict      raise ValueError('base used in int_init either too low or too high')    elif num > sum( set( power[1] for power in self.powers) ): #if num > sum of every power up to base**1000, can't represent it      raise ValueError('num is too large; must be <= sum_i=0^1000(base^i)') #sigma notation    elif num < 0: #no negatives      raise ValueError('num is too small; must be >= 0')    #special case for zero    if num == 0:      self.cols = 1      self.vals = {1: 0}    elif num % 1 != 0:      pass      #frac = fractions.Fraction(num)      #to-do: fractions    else:      for k in range(len(self.powers) - 1, 0 - 1, -1):  #counting down through self.powers        power = self.powers[k]                         if not self.vals: #not {} is True          if num >= power[1]: #go down till a power is smaller than the number            self.cols = power[0] #how many columns there are total            self.assign(power[0], num // power[1]) #how many of that power are in num            num %= power[1]        else:          self.assign(power[0], num // power[1])          num %= power[1]  def baseNum_init(self, _str):    #base included in the string in index 1 takes precedence over base argument of __init__    #if-statement b/c of special bases    self.base = self.rsbases[_str[1]] if _str[1] in self.rsbases else Base.val_from_char(_str[1])    baseNum   = _str[3:] #the important bit    self.cols = len(baseNum)    _col     = self.cols    #baseNum[::-1].index(digit) instead of a decrementing _col almost works, but repeating digits foil it    for digit in baseNum:      if Base.val_from_char(digit) >= self.base: #'8' is never allowed in a base-8 number        raise ValueError('Value in column ' + str(_col) + ' greater than allowed.')      else:        self.assign(_col, Base.val_from_char(digit))        _col -= 1  def dict_init(self, _dict):    if 'base' in _dict: #base can be supplied as argument to __init__ or as a dict key, if both dict takes precedence      self.base = _dict['base']      del _dict['base']    self.cols = len(_dict)    for col, val in _dict.items():      if val >= self.base:        raise ValueError('Value in column ' + str(col) + 'greater than allowed.')      else:        self.assign(col, val)    #ASSIGN  def assign(self, col, val):    self.vals[col] = val  #VAL DICTS  def val_from_char(char): #No self argument b/c they logically belong to the class, but they don't require an instance to work    return Base.rnums[char]  def char_from_val(val):    return Base.nums[val]  #TO AND FROM BASE-10  def base_ten(self):    result = 0     for col, val in self.vals.items():      result += self.base**(col-1) * val    return result  #basically undos str_init()  def __str__(self):    baseNum = ''    for m in range(len(self.vals), 0, -1):      baseNum += Base.char_from_val(self.vals[m])    baseId = Base.sbases[self.base] if self.base in Base.sbases else Base.char_from_val(self.base)    return '0' + baseId + 'b' + baseNum  def __format__(self):    pass    #on the to-do list  def __repr__(self):    return Base(' + self.__str__() + ')    # b/c of str_init, eval(repr(Base)) == Base#-------------#These are used in convert()nums = {0: '0', 1: '1', 2: '2', 3: '3', 4: '4', 5: '5', 6: '6', 7: '7', 8: '8', 9: '9', 10: 'A', 11: 'B', 12: 'C', 13: 'D', 14: 'E', 15: 'F', 16: 'G', 17: 'H', 18: 'I', 19: 'J', 20: 'K', 21: 'L', 22: 'M', 23: 'N', 24: 'O', 25: 'P', 26: 'Q', 27: 'R', 28: 'S', 29: 'T', 30: 'U', 31: 'V', 32: 'W', 33: 'X', 34: 'Y', 35: 'Z'}rnums = {'6': 6, 'Y': 34, '3': 3, 'M': 22, 'C': 12, '0': 0, '7': 7, 'X': 33, '1': 1, 'D': 13, 'W': 32, '9': 9, 'V': 31, 'U': 30, 'A': 10, 'L': 21, 'F': 15, 'O': 24, '4': 4, 'J': 19, 'Z': 35, 'I': 18, '5': 5, 'T': 29, 'P': 25, '2': 2, 'E': 14, 'R': 27, 'H': 17, 'S': 28, 'N': 23, '8': 8, 'B': 11, 'Q': 26, 'K': 20, 'G': 16}#Has all the same logic as Base.int_init()def convert(num, base=8):  if base > len(nums) or base < 2:    raise Exception  powers = tuple( (i + 1, base**i) for i in range(1000 + 1) )  if num == 0:    return Base(1, bigval = 0)  if num % 1 != 0:    pass    #frac = fractions.Fraction(num)  else:    result = None    for k in range(1, len(powers) + 1):      power = powers[-k]      if not result:        if num >= power[1]:          biggest = power          result = Base(biggest[0], base, nums[ num // biggest[1] ])          num %= biggest[1]      elif isinstance(result, Base):        result.assign(power[0], num // power[1])        num %= power[1]  return result#This used to be a placeholder Base to use where needed but it's not needed anymore#place = Base(1, base = 2)#TESTING#if __name__ == '__main__':baseToTheWhat = 5bases = ((2, 8, 16), range(2, 35 + 1), sorted( (2, 8, 6, 4, 35, 30, 16, 20, 15, 9, 29) ) ) #bases I want to trybaseIndex = 0 #which of those to use?for i in bases[baseIndex]:  for l in sorted(set([0, 1, 2, 5, 8, 9, 10, 11, 20, 40, 32, 63, 64, 65, 8**3 - 1, 8**3, 8**3 + 1, 100, 1000, 8**5 ]                         + [1,2,3,4,5,6,7,8,9,10]                          + [8**q for q in range(baseToTheWhat + 1)] + [8**q - 1 for q in range(baseToTheWhat + 1)] + [8**q + 1 for q in range(baseToTheWhat)]                         + [r**t + v for v in (-1, 0, 1) for t in range(baseToTheWhat) for r in bases[baseIndex]]                         + list(range(100))  )):    print({:10d} {:2d} {:20}.format(l, i, str(Base(l, base=i))))
Base object to convert positive numbers into other bases (2 <= base <= 35) in Python
python;performance;python 3.x;number systems