The str.isdigit() method returns True if all characters in the string are digits and the string contains at least one character. It returns False if the string is empty or contains letters, spaces, punctuation, negative signs (-), or decimal points (.).
The Unicode Numeric Hierarchy: str.isdigit() occupies the middle tier of Python's numeric checks: isdecimal() ⊂ isdigit() ⊂ isnumeric(). Unlike isdecimal(), isdigit() also returns True for superscripts (e.g. "²") and circled digits, but returns False for vulgar fractions (e.g. "½") and Roman numerals.
The isdigit() method is a built-in string method and takes no parameters:
is_digit_string = string.isdigit()
| Parameter | Type | Description |
|---|---|---|
| None | — | Takes no arguments. Passing arguments raises TypeError. |
| Return Value | bool |
Returns True if all characters are digits and length > 0; otherwise False. |
A string evaluates to True only when every single character is a digit. Any whitespace, punctuation, or sign returns False:
# Valid digit strings evaluate to True
print("012345".isdigit()) # True
print("2026".isdigit()) # True
print("007".isdigit()) # True
# False triggers: signs, decimals, spaces, letters, and empty strings
print("-42".isdigit()) # False: negative sign '-' is punctuation
print("+42".isdigit()) # False: positive sign '+' is punctuation
print("12.34".isdigit()) # False: decimal dot '.' is punctuation
print("12 34".isdigit()) # False: space ' ' is whitespace
print("abc12".isdigit()) # False: contains letters
print("".isdigit()) # False: empty string always returns False
Output:
True
True
True
False
False
False
False
False
False
"-50".isdigit() or "3.14".isdigit() to return True because they represent numbers. In Python, isdigit() validates individual string characters, not parsed mathematical quantities. Neither '-' nor '.' is a digit character.
Python provides three related string inspection methods. Understanding their subset relationship avoids subtle conversion bugs:
| String Representation | isdecimal() |
isdigit() |
isnumeric() |
Safe with int(s)? |
|---|---|---|---|---|
"123" (ASCII digits) | True | True | True | Yes: int("123") == 123 |
"१२३" (Devanagari 123) | True | True | True | Yes: int("१२३") == 123 |
"²" (Superscript 2) | False | True | True | No: raises ValueError |
"①" (Circled digit 1) | False | True | True | No: raises ValueError |
"½" (Fraction half) | False | False | True | No: raises ValueError |
"Ⅳ" (Roman numeral 4) | False | False | True | No: raises ValueError |
"一千" (Chinese 1000) | False | False | True | No: raises ValueError |
int(s), use str.isdecimal(). While "²".isdigit() returns True, calling int("²") fails with ValueError: invalid literal for int() with base 10: '²'.
The Unicode standard assigns the numeric digit property to many character forms beyond basic 0–9:
# Superscripts and subscripts
print("²".isdigit()) # True (Superscript 2)
print("³".isdigit()) # True (Superscript 3)
print("₄".isdigit()) # True (Subscript 4)
# Circled numbers (Unicode 1-20 circled digits)
print("①".isdigit()) # True (Circled digit 1)
print("⑩".isdigit()) # True (Circled digit 10)
# Eastern Arabic numerals
print("٣".isdigit()) # True (Arabic digit 3)
print("٥".isdigit()) # True (Arabic digit 5)
Output:
True
True
True
True
True
True
True
To validate signed integers or floating-point numbers without regex, use string stripping or partitioning patterns:
# 1. Validating signed integers (+/- allowed at start)
def is_signed_integer(s):
s = s.strip()
if s.startswith(("+", "-")):
s = s[1:]
return s.isdigit()
print("'-42' is signed int:", is_signed_integer("-42")) # True
print("'+100' is signed int:", is_signed_integer("+100")) # True
print("'-' is signed int: ", is_signed_integer("-")) # False
# 2. Validating positive float strings (exactly one decimal point)
def is_positive_float(s):
parts = s.strip().split(".")
return len(parts) == 2 and parts[0].isdigit() and parts[1].isdigit()
print("'12.34' is float: ", is_positive_float("12.34")) # True
print("'12.3.4' is float: ", is_positive_float("12.3.4"))# False
Output:
'-42' is signed int: True
'+100' is signed int: True
'-' is signed int: False
'12.34' is float: True
'12.3.4' is float: False
Authentication services verify that user-submitted verification codes consist of exactly 4 or 6 numeric digits without whitespace or letters:
def validate_otp(code, expected_length=6):
code = code.strip()
return len(code) == expected_length and code.isdecimal()
print("Valid OTP: ", validate_otp("849201")) # True
print("Invalid OTP:", validate_otp("849 01")) # False (space)
print("Short OTP: ", validate_otp("8492")) # False (length mismatch)
Output:
Valid OTP: True
Invalid OTP: False
Short OTP: False
Before applying the Luhn checksum algorithm, payment systems normalize formatted card strings (e.g. "4532-1488-2940-1284") by stripping delimiters and verifying digit-only integrity:
def prepare_card_digits(card_string):
cleaned = card_string.replace("-", "").replace(" ", "").strip()
if not (13 <= len(cleaned) <= 19):
return None
return cleaned if cleaned.isdigit() else None
print("Cleaned card digits:", prepare_card_digits("4532-1488-2940-1284"))
Output:
Cleaned card digits: 4532148829401284
The following production script implements an authentication OTP validator and a multi-format integer cleaner with automated unit test assertions:
class NumericValidator:
def is_valid_pin(self, pin_code, allowed_lengths=(4, 6)):
# Verifies numeric PIN containing standard ASCII digits
if not isinstance(pin_code, str):
return False
trimmed = pin_code.strip()
return len(trimmed) in allowed_lengths and trimmed.isdecimal()
def clean_integer_token(self, raw_token):
# Strips grouping commas and currency signs, returning parsed int or None
if not isinstance(raw_token, str):
return None
cleaned = raw_token.strip().replace(",", "").replace("$", "")
if cleaned.startswith(("+", "-")):
sign = -1 if cleaned[0] == "-" else 1
digits = cleaned[1:]
return sign * int(digits) if digits.isdigit() else None
return int(cleaned) if cleaned.isdigit() else None
# --- Unit Assertions and Verification ---
validator = NumericValidator()
# 1. PIN & OTP tests
assert validator.is_valid_pin("1234") is True
assert validator.is_valid_pin("984201") is True
assert validator.is_valid_pin("123") is False # Wrong length
assert validator.is_valid_pin("12345") is False # 5 digits not allowed
assert validator.is_valid_pin("123a") is False # Contains letter
assert validator.is_valid_pin("²²²²") is False # Superscripts rejected by isdecimal
assert validator.is_valid_pin("") is False
# 2. Integer token cleaning tests
assert validator.clean_integer_token("1,000,000") == 1000000
assert validator.clean_integer_token("$250") == 250
assert validator.clean_integer_token("-500") == -500
assert validator.clean_integer_token("+75") == 75
assert validator.clean_integer_token("12.34") is None # Float rejected
assert validator.clean_integer_token("N/A") is None
print("PIN '984201' valid: ", validator.is_valid_pin("984201"))
print("Cleaned '$1,500' token: ", validator.clean_integer_token("$1,500"))
print("All str.isdigit() unit assertions passed successfully!")
Open in Google Colab View on GitHub
Run all digit inspection examples interactively, evaluate Unicode superscripts, compare the numeric hierarchy, and test authentication PIN validators in Google Colab.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.