Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Python Module 2 – Values, variables, numbers and strings

Type conversion, number bases and bitwise operators

Convert between str, int, float and bool explicitly, read and write binary, octal and hexadecimal, and use Python's bitwise operators to work with bit flags.

  • Beginner
  • 25 minutes
  • Examples run with Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot

What you will learn

  • Convert between str, int, float and bool explicitly
  • Read and write binary, octal and hexadecimal values
  • Use &, |, ^, ~, << and >> to work with bit flags

Before you start

On this page

Python never turns text into a number, or a number into text, to make an operation work. "3" + 4 does not give 7 or "34": it raises TypeError, because a string and a number cannot be added. (Numbers of different types do mix: in 1 + 2.5 the int is widened to a float, so the result is 3.5.) When you need a number from text, or text from a number, you say so by calling the type you want. This lesson covers those conversions, the other ways of writing whole numbers (binary, octal and hexadecimal), and the operators that work on the individual bits of a number.

Converting between str, int and float

Each built-in type’s name converts a value to that type: int(), float(), str() and bool().

int(), float() and str() Python · conversions.py
# Each type's name converts a value to that type.
print(int("42") + 1, float("2.5") * 2, str(42) + "!")

# int() accepts spaces around the digits, a sign and underscores between digits.
print(int(" 7\n"), int("-12"), int("1_000"))

# From a float, int() cuts off the fraction (towards zero); round() rounds instead.
print(int(7.9), int(-7.9), round(7.9))
print(float("1e3"), float(" -0.5 "), float("inf"))
print(str(3.0), str(True), repr("3"))

# Text that is not written as a whole number raises ValueError, even "7.0".
for text in ["7.5", "7.0", "seven", ""]:
    try:
        int(text)
    except ValueError as error:
        print(error)
print(int(float("7.5")))

Output

43 5.0 42!
7 -12 1000
7 -7 8
1000.0 -0.5 inf
3.0 True '3'
invalid literal for int() with base 10: '7.5'
invalid literal for int() with base 10: '7.0'
invalid literal for int() with base 10: 'seven'
invalid literal for int() with base 10: ''
7

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 conversions.py

  • int(text) accepts spaces and line breaks around the digits, a + or - sign, and single underscores between digits. As the string-methods lesson showed, it reads decimal digits of other scripts too, such as "७".
  • int() reads whole numbers only, so text with a decimal point raises ValueError: int("7.5") does, and so does int("7.0"). Read it with float() first when that is what you mean: int(float("7.5")) is 7. (A float has limited precision, so a very long number loses its last digits on the way; decimal.Decimal keeps them all.)
  • From a float, int() drops the fraction and moves towards zero: int(7.9) is 7 and int(-7.9) is -7. Use round() when you want the nearest whole number.
  • float() also reads scientific notation ("1e3") and the special values "inf" and "nan".
  • str() gives the text you would see from print(). repr() gives the text you would write in code, which is why repr("3") shows the quotes: it tells a string "3" apart from the number 3.

True or false: bool() and text

bool() follows Python’s rules for truth: zero, None and anything empty are false, and almost everything else is true. For strings that means the only false string is "":

Every non-empty string is true Python · bool_trap.py
# bool() of a string asks only one question: is it empty?
print(bool("False"), bool("0"), bool(" "), bool(""))
print(bool(0), bool(0.0), bool(42), bool(None))

answer = "no"
if answer:
    print("This line runs, although the answer was no.")

# Text that means yes or no has to be read word by word.
print(answer.strip().casefold() in ("yes", "y", "true", "1"))

Output

True True True False
False False True False
This line runs, although the answer was no.
False

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 bool_trap.py

bool("False") is True, and so is bool("0"), because neither string is empty. Settings files, forms and command lines give you text such as “yes”, “off” or “0”, and the only way to read it as a yes or no is to compare it with the words you accept, as the last line does and as this lesson’s exercise asks you to do.

Binary, octal and hexadecimal

A whole number has no base of its own: base 10 is only how it is usually written. Python can write and read numbers in base 2 (binary), base 8 (octal) and base 16 (hexadecimal, with the digits a to f for 10 to 15):

One number, four ways of writing it Python · bases.py
n = 493

# bin(), oct() and hex() give text with a prefix; format types b, o, x and X leave it out.
print(bin(n), oct(n), hex(n))
print(f"{n:b} {n:o} {n:x} {n:X} {n:#x}")
print(f"{5:08b}", f"{n:_b}")

# In code, the same prefixes write a number in another base. It is still an ordinary int.
print(0b111101101, 0o755, 0x1ED, 0x1ED == 493)

# int(text, base) reads text in any base from 2 to 36; base 0 follows the prefix.
print(int("755", 8), int("1ed", 16), int("0x1ED", 16), int("0b101", 0))

# A colour code is three hexadecimal numbers, one each for red, green and blue.
colour = "#1E90FF"
print(int(colour[1:3], 16), int(colour[3:5], 16), int(colour[5:7], 16))

Output

0b111101101 0o755 0x1ed
111101101 755 1ed 1ED 0x1ed
00000101 1_1110_1101
493 493 493 True
493 493 493 5
30 144 255

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 bases.py

  • bin(), oct() and hex() return text with the prefixes 0b, 0o and 0x. The format types b, o, x and X write the digits without the prefix, # adds it, a width with 0 pads with zeros (08b for one byte), and _ groups the digits in fours.
  • The same prefixes write numbers in code: 0b111101101, 0o755 and 0x1ED are all the int 493, and nothing about the number remembers how it was written.
  • int(text, base) reads text in any base from 2 to 36 and accepts the matching prefix, so int("0x1ED", 16) works. With base 0, the prefix decides the base, as it does in code.
  • Hexadecimal is everywhere in computing because one hex digit is exactly four bits: colour codes such as #1E90FF are three numbers from 0 to 255, one each for red, green and blue.
Number Base Converter Convert any number between binary, octal, decimal and hexadecimal, with the working shown, to check your conversions.

Characters and their numbers: ord() and chr()

Every character is stored as its Unicode code point, a number. ord() returns the number of a one-character string and chr() returns the character for a number, so each undoes the other:

Code points and characters Python · ord_chr.py
# ord() gives the number of a character, and chr() the character of a number.
print(ord("A"), ord("a"), ord("0"), ord("अ"))
print(chr(65), chr(0x905), chr(0x1F40D))

for ch in "अआइ":
    print(ch, ord(ch), hex(ord(ch)), chr(ord(ch)) == ch)

Output

65 97 48 2309
A अ 🐍
अ 2309 0x905 True
आ 2310 0x906 True
इ 2311 0x907 True

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 ord_chr.py

The letters A to Z and a to z, and the digits 0 to 9, have consecutive numbers, which is why ord(ch) - ord("0") was a classic way to turn a digit into its value. chr() accepts numbers from 0 to 0x10FFFF, the largest code point, and raises ValueError outside that range.

Bitwise operators and bit flags

The bitwise operators treat a whole number as a row of bits and work on each bit position separately:

  • a & b (and) has a 1 bit wherever both a and b have one.
  • a | b (or) has a 1 bit wherever a, b or both have one.
  • a ^ b (exclusive or) has a 1 bit wherever exactly one of them has one.
  • ~a (invert) flips every bit; for Python’s ints, ~a equals -a - 1.
  • a << n (shift left) moves the bits n places to the left, which is the same as a * 2**n.
  • a >> n (shift right) moves the bits n places to the right and drops the lowest n, the same as a // 2**n.

Their everyday use is bit flags: several yes-or-no settings packed into one number, one bit each. Unix file permissions work that way, with read worth 4, write 2 and execute 1:

Permissions as bit flags Python · permissions.py
READ, WRITE, EXEC = 4, 2, 1

# | combines flags: each flag is one bit.
mode = READ | WRITE
print(mode, f"{mode:03b}")

# & tests a flag: the result is the flag itself, or 0.
print(mode & WRITE, mode & EXEC, bool(mode & WRITE))

# |= switches a flag on, &= ~ switches it off, ^= flips it.
mode |= EXEC
mode &= ~WRITE
print(mode, f"{mode:03b}")
mode ^= READ
print(mode, f"{mode:03b}")

# Three groups of three bits make a Unix permission such as 755.
owner, group, others = 7, 5, 5
permission = owner << 6 | group << 3 | others
print(permission, oct(permission), f"{permission:09b}")

Output

6 110
2 0 True
5 101
1 001
493 0o755 111101101

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 permissions.py

| switches flags on, & with a flag tests it (the result is the flag’s value or 0, so wrap it in bool() when you want True or False), & ~flag switches a flag off, and ^ flips one. Because one octal digit is exactly three bits, each digit of a permission such as 755 describes one group of users:

The Unix permission 755 as nine bits: 111 for the owner, 101 for the group and 101 for others, so 0o755 equals 0b111101101 and 493.ownerr14w12x114 + 2 + 17groupr14w02x114 + 15othersr14w02x114 + 150o755 = 0b111101101 = 493

One octal digit is three bits

Text description of the diagram

The diagram shows the file permission 755 as nine bits in three groups of three, labelled owner, group and others.

  • Each group has three bits for read (r), write (w) and execute (x), worth 4, 2 and 1.
  • The owner's bits are 1, 1 and 1, so the owner may read, write and execute: 4 + 2 + 1 is 7.
  • The group's bits are 1, 0 and 1, so the group may read and execute but not write: 4 + 1 is 5.
  • The bits for others are also 1, 0 and 1, which is 5 again.
  • Because one octal digit holds exactly three bits, the three digits 7, 5 and 5 can be read straight off the groups. The line at the bottom says that 0o755 in octal is 0b111101101 in binary and 493 in decimal: three ways of writing the same number.
Chmod Calculator Tick read, write and execute for each group and see the octal number, such as 755, and the symbolic mode.

Note

Python’s enum module has a Flag class that gives each bit a name and prints combinations readably; a later lesson on enums covers it. The operators underneath are the ones in this lesson.

Negative and very large numbers

Python’s ints have no fixed size, so they have no fixed number of bits either. The documentation describes the bitwise operators as working on two’s complement with an endless supply of sign bits:

Ints without a fixed width Python · infinite_bits.py
# Shifting left multiplies by a power of two; shifting right floor-divides.
print(1 << 10, 1001 >> 3, -1001 >> 3)

# ~x is -x - 1: Python ints behave as if they had endless sign bits.
print(~5, ~-6, -1 >> 10)
print(bin(-5), (-5).bit_count())

# A mask such as 0xFF keeps the lowest 8 bits, which gives the two's complement byte.
print(-5 & 0xFF, f"{-5 & 0xFF:08b}")

# bit_count() (Python 3.10 and newer) counts the ones; bit_length() gives the bits needed.
print((255).bit_count(), (255).bit_length(), (2**100).bit_length())

# Bitwise operators bind more tightly than comparisons, so this means (5 & 2) == 0.
print(5 & 2 == 0)

Output

1024 125 -126
-6 5 -1
-0b101 2
251 11111011
8 8 101
True

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 infinite_bits.py

  • Shifts multiply and floor-divide by powers of two. 1001 >> 3 is 125, while -1001 >> 3 is -126: the result is rounded down, exactly like -1001 // 8. And -1 >> 10 stays -1 because the sign bits never run out.
  • bin(-5) is '-0b101': a minus sign and the bits of 5, not a row of ones. To see a negative number as the byte a fixed-width language would store, mask it: -5 & 0xFF keeps the lowest eight bits, 251.
  • bit_length() is the number of bits needed to write a number without its sign, and bit_count() (Python 3.10 and newer) counts its 1 bits, ignoring the sign.
  • The bitwise operators bind more tightly than comparisons, so flags & WRITE == 0 means (flags & WRITE) == 0. Brackets still make such a line easier to read.
Programmer Calculator (Bitwise) Try the same operations at a fixed bit width (8, 16, 32 or 64 bits) to see where Python's endless ints differ from fixed-size ones.

Key takeaways

  • Convert explicitly with int(), float(), str() and bool(); int("7.5") raises ValueError, int(7.9) is 7 (towards zero) and round() rounds.
  • Every non-empty string is true, "False" included, so read yes-or-no text by comparing it with the words you accept.
  • bin(), oct() and hex() write prefixed text, int(text, base) reads it back, and 0b, 0o and 0x write numbers in code.
  • ord() and chr() convert between a character and its code point.
  • &, |, ^ and ~ work bit by bit and suit bit flags; << and >> multiply and floor-divide by powers of two.
  • Python’s ints act as if they had endless sign bits: mask with & 0xFF (and so on) when you need a fixed width.

Exercise

Exercise · Easy · Python

Read yes-or-no text and count the bits of a number

Write two small functions in bits.py.

The first, parse_bool(text), reads a yes-or-no setting, as it might appear in a settings file or a form. It returns True for "yes", "true", "on" and "1", and False for "no", "false", "off" and "0", in any mix of upper and lower case and with any spaces around the word: parse_bool(" Yes ") is True and parse_bool("OFF") is False. Any other text, the empty string included, raises ValueError. (bool(text) will not do: bool("False") is True.)

The second, count_set_bits(n), returns how many bits of the whole number n are 1: count_set_bits(255) is 8 and count_set_bits(2**100) is 1. Count them yourself with the bitwise operators & and >>: the function may not use bin(), format(), int.bit_count() or an f-string, and the sample tests check both. A negative n raises ValueError. It must also work for very large numbers, such as 2**4000 - 1.

Starter code · bits.py

def parse_bool(text):
    """Return True or False for words such as 'yes', 'OFF' or '1'; raise ValueError for anything else."""
    # Replace this line with your code.
    return bool(text)


def count_set_bits(n):
    """Return how many bits of n (0 or more) are 1, without bin(), format(), int.bit_count() or f-strings."""
    # Replace this line with your code.
    return 0
The sample tests · test_bits.py
import ast
import inspect

import bits
from bits import count_set_bits, parse_bool


def raises_value_error(function, value):
    """True when function(value) raises ValueError."""
    try:
        function(value)
    except ValueError:
        return True
    return False


def test_true_words():
    """reads yes, true, on and 1 as True, in any case and with spaces around"""
    for text in ["yes", "true", "on", "1", " Yes ", "TRUE", "On\n"]:
        assert parse_bool(text) is True, f"parse_bool({text!r})"


def test_false_words():
    """reads no, false, off and 0 as False"""
    for text in ["no", "false", "off", "0", "  NO", "False", "oFF"]:
        assert parse_bool(text) is False, f"parse_bool({text!r})"


def test_other_text():
    """raises ValueError for any other text"""
    for text in ["", "   ", "maybe", "y", "2", "yes please"]:
        assert raises_value_error(parse_bool, text), f"parse_bool({text!r}) should raise ValueError"


def test_small_numbers():
    """counts the 1 bits of small numbers"""
    assert count_set_bits(0) == 0
    assert count_set_bits(1) == 1
    assert count_set_bits(0b1011) == 3
    assert count_set_bits(255) == 8
    assert count_set_bits(256) == 1


def test_large_numbers():
    """counts the 1 bits of very large numbers"""
    assert count_set_bits(2**100) == 1
    assert count_set_bits(2**100 - 1) == 100
    assert count_set_bits(2**4000 - 1) == 4000
    assert count_set_bits(10**30) == (10**30).bit_count()


def test_negative():
    """raises ValueError for a negative number"""
    assert raises_value_error(count_set_bits, -1)
    assert raises_value_error(count_set_bits, -255)


def test_no_shortcuts():
    """counts the bits without bin(), format(), bit_count() or an f-string"""
    tree = ast.parse(inspect.getsource(bits.count_set_bits))
    for node in ast.walk(tree):
        assert not (isinstance(node, ast.Name) and node.id in ("bin", "format")), "bin() and format() are not allowed"
        assert not (isinstance(node, ast.Attribute) and node.attr in ("bit_count", "format")), "bit_count() and str.format() are not allowed"
        assert not isinstance(node, ast.JoinedStr), "f-strings are not allowed"


def test_bitwise_operators():
    """counts the bits with the bitwise operators & or >>"""
    tree = ast.parse(inspect.getsource(bits.count_set_bits))
    used = {type(node.op) for node in ast.walk(tree) if isinstance(node, (ast.BinOp, ast.AugAssign))}
    assert ast.BitAnd in used or ast.RShift in used, "look at the bits with & or >>, not with % or //"
A hint

For parse_bool(), clean the text with strip() and casefold() first, then test it with in against a tuple of the accepted words. For count_set_bits(), n & 1 is the lowest bit of n (0 or 1) and n >> 1 drops that bit, so a loop that adds n & 1 and then shifts n until it is 0 visits every bit once.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Check yourself

5 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 5 What does permissions.py print?

    What does this program print? Choose one answer.

    READ, WRITE, EXEC = 4, 2, 1
    
    # | combines flags: each flag is one bit.
    mode = READ | WRITE
    print(mode, f"{mode:03b}")
    
    # & tests a flag: the result is the flag itself, or 0.
    print(mode & WRITE, mode & EXEC, bool(mode & WRITE))
    
    # |= switches a flag on, &= ~ switches it off, ^= flips it.
    mode |= EXEC
    mode &= ~WRITE
    print(mode, f"{mode:03b}")
    mode ^= READ
    print(mode, f"{mode:03b}")
    
    # Three groups of three bits make a Unix permission such as 755.
    owner, group, others = 7, 5, 5
    permission = owner << 6 | group << 3 | others
    print(permission, oct(permission), f"{permission:09b}")
    Show the answer to question 1

    Answer: it prints

    6 110
    2 0 True
    5 101
    1 001
    493 0o755 111101101

    READ | WRITE is 4 + 2 = 6. mode & WRITE is the flag itself (2) when it is set and 0 when it is not. Switching EXEC on and WRITE off gives 4 + 1 = 5, and flipping READ leaves 1. The last line builds 0o755, which is 493 in decimal.

  2. Question 2 of 5 What does this print?

    Read the code, then choose one answer.

    print(int(" 42 ") + int("101", 2))
    Show the answer to question 2

    Answer: 47

    int() ignores spaces around the digits, so the first part is 42, and int("101", 2) reads binary, which is 5. The sum is 47.

  3. Question 3 of 5 What is bool("False")?

    Choose one answer.

    Show the answer to question 3

    Answer: True

    bool() of a string only checks whether the string is empty. "False" has five characters, so it is true. To read words such as "false" or "off", compare the text with them yourself.

  4. Question 4 of 5 0o755 is a number written in octal. What is it in decimal?

    Type a number.

    Show the answer to question 4

    Answer: 493

    7 × 64 + 5 × 8 + 5 = 448 + 40 + 5 = 493. Each octal digit stands for three bits, so 0o755 is also 0b111101101.

  5. Question 5 of 5 Which of these turn the text "ff" (or "0xff") into the number 255?

    Choose every answer that is right.

    Show the answer to question 5

    Answer:

    • int("ff", 16)
    • int("0xff", 16)
    • int("0xff", 0)

    int(text, 16) reads hexadecimal and also accepts the 0x prefix; base 0 works out the base from the prefix. Without a base, int("ff") expects decimal digits and raises ValueError, and hex() goes the other way: it takes a number and returns text, so it raises TypeError for a string.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.