Home/Python/Data Types I

Data Types I

Scalar values, text, conversion, and a first look at lists

Values and types

A value's type determines its available operations. Use type() to inspect it:

Python
print(12 + 3)       # 15
print("12" + "3")   # 123
print(type(12))     # <class 'int'>
print(type("12"))   # <class 'str'>
TypeExampleCommon use
int3, 125500Counts and exact whole numbers
float0.125, 2.5Ratios and measurements
boolTrue, FalseDecisions and flags
str"payments-worker"Names, identifiers, text
NoneTypeNoneAn absent value
complex3 + 4jReal and imaginary components

All these values are objects. Assignment binds a name to an object; reassignment changes what that name refers to:

Python
workers = "3"
workers = int(workers)
print(workers)        # 3
print(type(workers))  # <class 'int'>

int() converts the text to an integer; it does not change the original string's type.

Integers

Integers support exact whole-number arithmetic:

OperationExpressionResult
Addition8 + 311
Subtraction8 - 35
Multiplication8 * 324
Division8 / 3approximately 2.6667, a float
Floor division8 // 32
Remainder8 % 32
Exponentiation2 ** 101024

Multiplication and division take precedence over addition and subtraction. Use parentheses to change the grouping: 2 + 3 * 4 is 14; (2 + 3) * 4 is 20.

Comparisons such as <, >=, ==, and != produce booleans. = assigns; == compares:

Python
workers = 3
capacity = workers * 20
occupied = 47
print(capacity - occupied)  # 13 available slots
print(occupied < capacity)  # True

Use division and remainder when converting units:

Python
timeout_ms = 125500
print(timeout_ms / 1000)   # 125.5 seconds
print(timeout_ms // 1000)  # 125 whole seconds
print(timeout_ms % 1000)   # 500 remaining milliseconds

/ produces a float. // rounds down; int() truncates toward zero, so -7 // 3 is -3 while int(-7 / 3) is -2. divmod(timeout_ms, 1000) returns the quotient and remainder together as a tuple, covered in Data Types II.

Python integers grow as needed, subject to memory. They do not silently wrap at a fixed 32-bit or 64-bit boundary, though a database or external format may impose its own limits.

Floats and conversion

Floats represent approximate real values, useful for ratios and measurements:

Python
sample_rate = float(" 0.125 ")
print(800 * sample_rate)                # 100.0
print(f"Sample rate: {sample_rate:.1%}")  # Sample rate: 12.5%

F-strings insert expressions inside braces. :.1% formats a ratio as a percentage with one decimal place; :.2f formats a number with two decimal places. Formatting changes the display, not the stored value.

Conversion rules depend on the input:

ExpressionResult
int("12")12
int(3.5)3, truncated toward zero
int("3.5")Raises ValueError
float("3.5")3.5

Complex numbers use j for the imaginary component. (3 + 4j).real is 3.0, .imag is 4.0, and abs(3 + 4j) is 5.0. They are used in areas such as signal processing and electrical calculations.

Booleans and missing values

Comparisons produce True or False. Combine conditions with and, or, and not:

Python
workers = 3
sample_rate = 0.125
has_capacity = workers > 0
valid_rate = 0 <= sample_rate <= 1
print(has_capacity and valid_rate)  # True
print(not has_capacity)            # False

and and or short-circuit: the right expression runs only when needed. They return an operand, which is not necessarily a boolean.

None represents absence. Keep it distinct from valid zero or false values:

Python
fallback = None  # Inherit the environment's routing default
workers = 0
enabled = False
print(fallback is None)             # True
print(workers == 0, enabled is False)  # True True

Use is None for absence and == for ordinary value comparisons. is checks object identity.

bool(value) tests truthiness. Zero, empty strings and containers, and None are falsey; nonempty strings are truthy.

Strings

Strings are immutable sequences of Unicode code points. Single and double quotes both create strings; \n and \t represent a newline and tab. String methods return values without changing the original.

Python
raw_name = " \tPayments-Worker\n"
name = raw_name.strip()
print(name)                            # Payments-Worker
print(raw_name == name)                # False
print(name[0], name[-1], name[:8])      # P r Payments

Indexes start at zero; negative indexes count from the end. A slice excludes its end position. An out-of-range index raises IndexError, while a slice can extend past the end. Indexing a string returns a string; Python has no separate character type.

Common operations:

Python
print(" TrUe ".strip().lower())             # true
print(len("worker"))                       # 6
print("Worker" in "Payments-Worker")        # True
print("api-worker".startswith("api-"))      # True
print("settings.json".endswith(".json"))    # True
print("worker_old".replace("_old", "_v2"))  # worker_v2
print("svc-worker".removeprefix("svc-"))    # worker

Normalize case only when the field's rules allow it. .casefold() handles more Unicode cases than .lower() for case-insensitive matching.

.strip() removes whitespace from the ends. With an argument, it removes any of those characters, not an exact prefix or suffix. Use .removeprefix() or .removesuffix() for exact text.

Split text into a list, then join it

split() produces a list: an ordered sequence with indexed elements.

Python
raw_labels = "  api   nightly\turgent  "
cleaned = raw_labels.strip()
labels = cleaned.split()
print(labels)             # ['api', 'nightly', 'urgent']
print(labels[0], len(labels))  # api 3
print(", ".join(labels))   # api, nightly, urgent

Without an argument, split() groups whitespace and omits empty fields. It already handles surrounding whitespace, so the preceding strip() is optional here.

join() inserts its separator between strings; every list element must be a string. An explicit split separator preserves empty fields:

Python
print("east,,west".split(","))  # ['east', '', 'west']
print("".split())              # []
print(", ".join([]))           # Empty line

An empty list has nothing to index, but it can still be joined. Data Types II covers list updates and other containers.

Example: configuration values

Choose conversions by field: counts need arithmetic, identifiers need exact preservation, and textual flags need explicit interpretation.

Python
name = "  payments-worker  ".strip()
deployment_id = "000042"
enabled = " TrUe ".strip().lower() == "true"
labels = " payments   nightly ".split()
print(f"{name} [{deployment_id}]")  # payments-worker [000042]
print(enabled, ", ".join(labels))  # True payments, nightly

The same rules apply to environment variables, form fields, CSV cells, and command-line arguments.

Gotchas

Float precision

Binary64 floats cannot represent every integer beyond 2 ** 53 exactly:

Python
identifier = 2 ** 53 + 1
transported = float(identifier)
print(int(transported))                # 9007199254740992
print(identifier - int(transported))   # 1
print(0.1 + 0.2 == 0.3)                # False

Converting back cannot recover lost precision. Keep identifiers as integers or strings across storage and transport. For measured quantities, compare floats using tolerances appropriate to the problem.

For money, integer minor units or Decimal may better match the contract. Construct decimals from text and specify rounding rules: rounding line items and rounding the total can produce different results.

Textual booleans

Truth testing does not parse a string's meaning:

Python
print(bool("false"))                       # True: nonempty string
print(" FALSE ".strip().lower() == "true")  # False
print(True + True)                         # 2: booleans act as integers

The comparison is sufficient only when input is known to be true or false. Unknown spellings need a separate validation policy. Likewise, a count API must decide whether to accept booleans as numbers.

Characters and bytes

String length counts code points, not necessarily displayed symbols or encoded bytes:

Python
print(len("café"), len("café".encode("utf-8")))  # 4 5
print(len("e\u0301"))                          # 2: e + combining accent

A display limit and a network byte limit need different policies. encode() produces bytes without changing the original text.

Repeated concatenation of growing strings can require repeated allocation and copying. When pieces are already available, join() is often clearer and avoids repeatedly rebuilding the prefix.

Put it into practice

Implement the exercise in a full-screen workspace, then run its tests.

Open practice
Next lessonData Types II