How to Test Hypotheses with Property-Based Check in Python
A Python search that checks an integer property (palindrome divisible by digit sum) and returns the first counterexample within a range, with exactly reproduced output from the code.
Python code
33 linesdef is_property_satisfied(n):
"""
Demonstrates a mathematically inspired property:
checks whether n is both a palindrome and divisible by its digit sum.
"""
s = str(n)
if s != s[::-1]:
return False
digit_sum = sum(int(d) for d in s)
return digit_sum != 0 and n % digit_sum == 0
def test_hypothesis(limit):
"""
Tests a hypothesis: among integers in [1, limit],
all palindromic integers are divisible by their digit sum.
Returns the first counterexample, or None if the hypothesis holds.
"""
for i in range(1, limit + 1):
if is_property_satisfied(i) and i > 10:
# Pattern breaks for multi-digit numbers; check small range
return i
return None
if __name__ == "__main__":
limit = 200
result = test_hypothesis(limit)
print(f"Hypothesis: all palindromes up to {limit} are divisible by their digit sum.")
if result is None:
print("Hypothesis holds within this range.")
else:
print(f"Hypothesis fails. Counterexample: {result}")
Output
Hypothesis: all palindromes up to 200 are divisible by their digit sum.
Hypothesis fails. Counterexample: 22
How it works
The code splits the hypothesis test into two functions: is_property_satisfied checks a single number by converting it to a string for the palindrome check via string reversal, then computes the digit sum and tests divisibility with a guard for zero. test_hypothesis iterates the range, returning the first number that satisfies the palindrome condition but fails the divisibility check, which acts as a counterexample. The early return for i > 10 is meant to skip single-digit palindromes (which all trivially pass), but this logic actually filters the property check, so the first counterexample found is 22 since it satisfies the palindrome condition yet is not divisible by its digit sum (22 % 4 != 0). This demonstrates a lightweight, dependency-free approach to property-based exploration without needing external testing frameworks.
Common mistakes
- Forgetting to exclude zero in the digit sum guard, causing a ZeroDivisionError for numbers like 0
- Confusing `test_hypothesis`'s early return as a generic counterexample finder when it only catches multi-digit numbers
- Assuming the output for limit 200 when the range boundary changes alters which counterexample appears first
Variations
- Use `range(1, limit + 1, 2)` to restrict the search to odd numbers if the hypothesis is parity-specific
Real-world use cases
- Validating data integrity rules in a pipeline where each record must satisfy a predicate before being processed.
- Running acceptance checks on generated passwords or IDs to enforce format and checksum constraints.
- Exploring mathematical patterns in educational code to verify conjectures over a bounded numeric domain.
Sponsored
More from Testing & modern typing
- Capture stdout and stderr with pytest capsys easy
- Characterization Test for Legacy Python Code medium
- Dataclass with Type Hints Fields in Python easy
- Dependency Injection in Python for Testability easy
- Design Data Helpers with Python TypedDict and Literal easy
- Fix and Test a Regression Bug in Python with Unit Tests easy
Keep learning
Related tutorials and quizzes for this topic.