How to Check Whether a Number Is Prime in Python
Check whether an integer is prime with Python’s standard library. Learn the square-root rule, handle edge cases, and choose an approach for repeated checks.

To check whether an integer n is prime, return False when n < 2, then try dividing it by every integer from 2 through the floor of its square root. If any division has remainder zero, n is composite; if none does, it is prime. Python’s math.isqrt gives that boundary exactly.
from math import isqrt
def is_prime(n: int) -> bool:
if n < 2:
return False
for divisor in range(2, isqrt(n) + 1):
if n % divisor == 0:
return False
return True
print(is_prime(2)) # True
print(is_prime(17)) # True
print(is_prime(18)) # False
This approach uses only the Python standard library and works for arbitrarily large Python integers, though the amount of trial division grows with the square root of the input. math.isqrt was added in Python 3.8 and returns the floor of the exact square root. Python math documentation.
1. What “prime” means and which values count
A prime number is an integer greater than 1 whose only positive divisors are 1 and itself. The integer 2 is prime and is the only even prime. Numbers below 2—including negative integers, 0, and 1—are not prime. That is why the first line of the function handles n < 2 before taking a square root or starting the divisor loop.
The function’s annotation says it expects an int. It does not convert strings or floats. Keeping the contract explicit avoids silently changing inputs, such as treating 7.9 as 7. If values arrive from user input, parse and validate them at the boundary:
raw = input("Enter an integer: ")
try:
candidate = int(raw)
except ValueError:
print("Please enter a whole number.")
else:
print("prime" if is_prime(candidate) else "not prime")
2. Why checking through the square root is enough
Suppose a composite number can be written as a * b. If both factors were greater than the square root of the number, their product would be greater than the number. So every composite integer has at least one factor at or below its square root. Testing all candidates up to that point will find a factor if one exists.

For example, 36 has factor pairs 2×18, 3×12, 4×9, and 6×6. The pairs mirror around 6, which is the square root. There is no need to check 7 through 35 after checking candidates through 6.
The expression range(2, isqrt(n) + 1) includes the square-root boundary when it is an integer. Python’s range excludes its stop value, so the + 1 matters. For 49, isqrt(49) is 7; without the increment, the loop would stop before testing 7 and incorrectly call 49 prime.
3. A complete function with input validation
For a reusable function, reject non-integer inputs deliberately. Python’s bool is a subclass of int, so decide whether booleans should be accepted as numbers. The stricter version below rejects them too:
from math import isqrt
def is_prime(n: int) -> bool:
"""Return whether n is a prime integer.
Raises TypeError for values that are not integers, including bool.
"""
if isinstance(n, bool) or not isinstance(n, int):
raise TypeError("n must be an integer")
if n < 2:
return False
for divisor in range(2, isqrt(n) + 1):
if n % divisor == 0:
return False
return True
for value in (-5, 0, 1, 2, 3, 4, 17, 49):
print(f"{value}: {is_prime(value)}")
For many scripts, the shorter function at the top is sufficient. Add the explicit type check when values can come from untrusted input, a loosely typed interface, or a larger application where accidental floats and booleans would indicate a programming error.
4. A small optimization: skip even divisors
After checking 2, an even input is composite; for an odd input, no even divisor can be a new factor. You can therefore test 2 once and then only odd candidates:
from math import isqrt
def is_prime_skip_evens(n: int) -> bool:
if n < 2:
return False
if n == 2:
return True
if n % 2 == 0:
return False
for divisor in range(3, isqrt(n) + 1, 2):
if n % divisor == 0:
return False
return True
This reduces the number of trial candidates after the initial even check. It retains the same worst-case shape: a prime input requires testing candidates up to its square root. Choose the straightforward version when readability is the priority; use the odd-candidate version if this routine is a real hot path and you have measured your workload. No universal input-size crossover follows from the algorithm alone.
5. Common mistakes and troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| 0 or 1 returns prime | The loop starts at 2, so it performs no tests for those values. | Guard with if n < 2: return False before the loop. |
| A perfect square such as 49 returns prime | The upper bound was excluded by range. |
Use isqrt(n) + 1 as the stop value. |
| The program gets a type error | math.isqrt requires a nonnegative integer; a float or string was passed. |
Parse text using int, validate types, and handle values below 2 first. |
| Negative input raises an error | isqrt was called before checking the domain. |
Return false for n < 2 before computing the square root. |
| The function is slow for a very large prime | A prime has no early factor to find, so every candidate through the square root is checked. | For bounded batches use a sieve; for very large single values, select a suitable primality algorithm based on verified requirements. |
isqrt cannot be imported |
The interpreter is older than Python 3.8. | Upgrade Python where possible. Avoid replacing it with a floating-point square root for exact large-integer boundaries. |
6. Complexity, performance, and reliability
In the worst case, trial division checks on the order of the square root of n candidates. The odd-only form roughly halves the candidate count after checking 2, but does not change that growth pattern. Composite numbers can finish sooner when a small factor is found; primes and composites whose smallest factor is large do more work.
The method is deterministic: for integer inputs it returns the correct result because it checks the complete necessary factor range. It has constant auxiliary space apart from integer arithmetic and loop state. Python integers can grow beyond machine-word sizes, and isqrt avoids a floating-point rounding boundary, but enormous values can still require too many trial divisions to be practical.
Do not infer a cryptographic suitability or security guarantee from this simple function. Cryptographic-size primality checking has different algorithm and security requirements; the sources for this guide do not establish a recommended method for that domain. Use an appropriately reviewed library and documented requirements when the result affects security.
7. Checking many numbers: use a sieve when the range is known
If you need primality for many values up to a fixed maximum, independently running trial division repeats work. A sieve marks multiples and leaves the primes in the range. Here is a standard Sieve of Eratosthenes that returns all primes through limit:

from math import isqrt
def primes_up_to(limit: int) -> list[int]:
if limit < 2:
return []
is_prime = bytearray(b"\x01") * (limit + 1)
is_prime[0:2] = b"\x00\x00"
for candidate in range(2, isqrt(limit) + 1):
if is_prime[candidate]:
start = candidate * candidate
count = ((limit - start) // candidate) + 1
is_prime[start:limit + 1:candidate] = b"\x00" * count
return [number for number, flag in enumerate(is_prime) if flag]
print(primes_up_to(30))
A sieve is useful when the upper bound is known and you need many answers in that interval. It stores a marker for each value up to the limit, so memory grows with the limit. For a few isolated queries over a wide domain, trial division may be simpler because it does not allocate an array proportional to the maximum value. Choose by input count, known bound, memory budget, and simplicity. The available evidence does not provide a universal crossover benchmark.
8. Tests and useful edge cases
A compact set of examples should include the lower boundary, the only even prime, a composite, a perfect square, and a larger prime. These assertions can live alongside the function during development:
assert is_prime(0) is False
assert is_prime(1) is False
assert is_prime(2) is True
assert is_prime(3) is True
assert is_prime(4) is False
assert is_prime(49) is False
assert is_prime(97) is True
For the strict version, also check invalid values such as is_prime(3.0) and is_prime(True) raise TypeError. In production code, put tests in the project’s usual test suite and keep input conversion separate from the mathematical predicate.
9. Or skip the browser setup
This programming question does not need a browser screenshot. If you are building a workflow that does need website captures—for example, checking rendered pages in documentation or content tooling—ScreenshotNeo is a website screenshot API and MCP server from ScreenshotNeo. One GET request returns a PNG, JPEG, WebP, or PDF. See the API documentation for parameters.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Cookie banners are accepted and removed before the capture, along with supported newsletter popups and chat widgets. Bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing; response headers say the page verdict and whether it was billed. An MCP server gives AI agents tools to take screenshots, inspect page information, and capture PDFs. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.
Sign up free for 1,000 screenshots a month, with no card required.
10. Frequently asked questions
Is 1 a prime number?
No. A prime has exactly two positive divisors, while 1 has only one.
Can I use this with a negative integer?
Yes. The function returns False for every integer below 2.
Should I use math.sqrt instead?
Use math.isqrt for the loop bound. It returns an exact integer floor and avoids using a floating-point approximation as a boundary.
Does the code work on every Python version?
The displayed implementation using math.isqrt requires Python 3.8 or newer. Earlier versions need a different exact-boundary strategy or an upgrade.
How should I check a list of values?
For many values up to a known maximum, build a sieve once. For a small number of unrelated values, call the single-number function for each.


