4.5 Iterating, Sorting, and Comparing Strings Against Other Types

Key Takeaways

  • `str` objects have no `.sort()` method — calling `"text".sort()` raises `AttributeError`; `sorted()` is the built-in that accepts a string and always returns a **list of single-character strings**, never a string.
  • `list.sort()` mutates a list of strings in place and returns `None`, whereas `sorted()` leaves its argument untouched and returns a new list, so `words = words.sort()` destroys the data.
  • Default string sorting is by Unicode code point, which places every ASCII uppercase letter before every lowercase letter; `key=str.lower` restores case-insensitive alphabetical order.
  • Equality between a string and a number is always defined and always `False` (`'2' == 2`), but ordering comparisons such as `'2' < 3` raise `TypeError: '<' not supported between instances of 'str' and 'int'`.
  • Iterating a string with `for ch in s` yields one-character strings, so a string is an iterable of strings — there is no separate character type in Python.
Last updated: August 2026

Iterating, Sorting, and Comparing Strings Against Other Types

Objective 3.3 lists .sort() and sorted() among the built-in string tools, and objective 3.2 names iteration and comparison "against strings and numbers". All three share one root idea that the exam probes relentlessly: a string is a sequence, but it is not a list, and it is immutable. This section works through what that means for looping, ordering, and cross-type comparisons.


1. Why This Sits in the Strings Block

Objective PCAP-31-03 3.3 explicitly names .sort() and sorted() in its list of built-in string methods, and objective 3.2 explicitly names iterating through strings and comparing (against strings and numbers). Those three items are grouped here because they share a single exam trap: strings are sequences, but they are not lists, and the exam repeatedly probes the boundary between the two.

2. Iterating Through a String

A string is an iterable. A for loop over a string yields its characters left to right, and every yielded value is itself a str of length 1 — Python has no distinct character type.

word = "abc"

for ch in word:
    print(ch, type(ch).__name__, len(ch))
# a str 1
# b str 1
# c str 1

Because each character is a full string, all string operations work on it immediately:

title = "python 3"

upper_count = 0
for ch in title:
    if ch.isalpha() and ch.islower():
        upper_count += 1
print(upper_count)   # 6

Two supporting built-ins appear constantly in exam items:

  • enumerate(s) yields (index, character) tuples: list(enumerate("ab"))[(0, 'a'), (1, 'b')].
  • reversed(s) returns a lazy reversed object, not a string. list(reversed("abc"))['c', 'b', 'a'], and "".join(reversed("abc"))'cba'. The slice idiom "abc"[::-1] produces the reversed string directly and is usually the intended answer.

Exam Trap: print(reversed("abc")) displays something like <reversed object at 0x...>. If an option offers 'cba' for a bare reversed() call without a join() or list(), it is wrong.

3. Sorting: sorted() Versus .sort()

str Has No .sort() Method

This is the single most misread line in the syllabus. .sort() is a list method. Strings are immutable, so no in-place sort can exist for them:

s = "banana"
s.sort()
# AttributeError: 'str' object has no attribute 'sort'

sorted() Accepts a String but Returns a List

The built-in sorted(iterable) consumes any iterable — including a string — and returns a new list:

print(sorted("banana"))
# ['a', 'a', 'a', 'b', 'n', 'n']

print("".join(sorted("banana")))
# 'aaabnn'

To get a sorted string back you must re-join the list. Forgetting the join() is the classic distractor.

list.sort() Returns None

When the data is already a list of strings, .sort() mutates it in place and returns None:

words = ["b", "a"]
result = words.sort()
print(result)   # None
print(words)    # ['a', 'b']
# The destructive anti-pattern the exam loves:
names = ["Zoe", "Adam"]
names = names.sort()   # names is now None, the data is gone
ExpressionMutates original?Return valueWorks on a str?
sorted(x)NoNew listYes
sorted(x, reverse=True)NoNew list, descendingYes
x.sort()YesNoneNo — AttributeError
x.sort(reverse=True)YesNoneNo — AttributeError

4. Sort Order Is Code-Point Order

Sorting uses the same lexicographical rules as the < operator: characters are ordered by Unicode code point. In ASCII, uppercase AZ occupy 65–90 and lowercase az occupy 97–122, so every uppercase letter sorts before every lowercase letter:

print(sorted("Zebra"))
# ['Z', 'a', 'b', 'e', 'r']

print(sorted(["delta", "Echo", "alpha"]))
# ['Echo', 'alpha', 'delta']

The key parameter fixes this by supplying the value each element is ranked by, without altering the values that are returned:

print(sorted(["delta", "Echo", "alpha"], key=str.lower))
# ['alpha', 'delta', 'Echo']

print(sorted(["delta", "Echo", "alpha"], key=len))
# ['Echo', 'delta', 'alpha']

Digits (48–57) sort before letters, and the space character (32) sorts before everything printable:

print(sorted("hello world"))
# [' ', 'd', 'e', 'h', 'l', 'l', 'l', 'o', 'o', 'r', 'w']

Numeric strings sort as text, not as numbers, unless a key converts them:

print(sorted(["10", "9", "100"]))
# ['10', '100', '9']

print(sorted(["10", "9", "100"], key=int))
# ['9', '10', '100']

5. Comparing Strings Against Numbers

Python draws a hard line between equality comparisons and ordering comparisons across types.

Equality Is Always Defined

== and != never raise for mismatched types. A str is never equal to an int, even when it looks identical:

print("2" == 2)    # False
print("2" != 2)    # True
print("" == 0)     # False

Ordering Raises TypeError

<, <=, >, and >= are undefined between str and numeric types:

print("2" < 3)
# TypeError: '<' not supported between instances of 'str' and 'int'

The same rule propagates through anything that orders values internally, which is why a mixed-type list cannot be sorted and why min()/max() fail on mixed arguments:

sorted(["a", 1])
# TypeError: '<' not supported between instances of 'int' and 'str'
ExpressionResult
"2" == 2False
"2" != 2True
"2" < 3TypeError
sorted(["a", 1])TypeError
"10" < "9"True (both strings — code-point comparison of '1' vs '9')
int("10") < int("9")False

Exam Trap: '10' < '9' is True because '1' (49) precedes '9' (57). Candidates who mentally convert numeric strings to integers pick the wrong option almost every time.

6. Putting It Together

A frequent scenario item asks for a canonical, case-insensitive, de-duplicated signature of a string. Every tool above appears at once:

raw = "Mississippi"

print(sorted(raw))                 # ['M', 'i', 'i', 'i', 'i', 'p', 'p', 's', 's', 's', 's']
print(sorted(raw, key=str.lower))  # ['i', 'i', 'i', 'i', 'M', 'p', 'p', 's', 's', 's', 's']
print("".join(sorted(set(raw))))   # 'Mips'

Note how sorted(raw) places the uppercase 'M' first on code point, while key=str.lower ranks it as 'm' and pushes it past the 'i' characters — the returned element is still the original uppercase 'M'.

Loading diagram...
Choosing Between sorted(), .sort(), and Cross-Type Comparison Outcomes
Test Your Knowledge

What is printed by the following code?

result = sorted("banana")
print(result)

A
B
C
D
Test Your Knowledge

Consider the following code:

labels = ["delta", "Echo", "alpha"]
output = labels.sort()
print(output, labels)
What is printed?

A
B
C
D
Test Your Knowledge

Which of the following expressions raises a TypeError in Python 3?

A
B
C
D
Test Your Knowledge

Given text = "Zebra", which expression evaluates to the string 'aberZ'?

A
B
C
D