I think this code is pretty good, I like the docstrings!
Some things strike me as a bit awkward though,
- Stay DRY
These log functions are really similar, the only thing that differentiates is the operators
You could make the operator an argument of the function, then you'd only have 1 function
You'd then need a lookup table for the operators so you know when to subtract or add.
- The are better testing modules
Assertions are fine, but are not the best testsuites, there are some good modules for testing in python (I'm a big fan of doctest) but unittest is also pretty good
So I would rewrite the log functions to this:
import operator
import doctest
MATH_OPER = {
operator.gt: operator.add,
operator.ge: operator.add,
operator.lt: operator.sub,
operator.le: operator.sub
}
def log_oper(valueop, basevalue, opbase):
"""
The logarithmic function with an operator
Test all operators
>>> log_oper(3operator.gt, 23, operator.gt2)
2
>>> log_oper(3operator.ge, 23, operator.ge2)
2
>>> log_oper(3operator.lt, 23, operator.lt2)
1
>>> log_oper(3operator.le, 23, operator.le2)
1
Test assertion Exception
>>> log_oper(operator.le, -1, 2, operator.le)
Traceback (most recent call last):
...
AssertionError: Logarithm is only defined for numbers greater than zero (the power approaches negative infinity as the value approaches zero)
"""
add_or_sub = MATH_OPER[op]
assert value > 0, 'Logarithm is only defined for numbers greater than zero (the power approaches negative infinity as the value approaches zero)'
power, limit = _lut[base][value.bit_length()]
return add_or_sub(power, op(value, limit))
if __name__ == '__main__':
max_value = 10**6
base = 2
init_upto_bitlen(max_value.bit_length(), base)
doctest.testmod()