杰瑞科技汇

Python getitem如何通过index实现索引访问?

Of course! In Python, the term "getitem" almost always refers to the special "dunder" (double underscore) method __getitem__. This method is the magic behind the square bracket notation ([]) used for accessing items in a sequence or mapping.

Python getitem如何通过index实现索引访问?-图1
(图片来源网络,侵删)

Let's break it down.

The Core Concept: __getitem__

When you write my_object[index], Python secretly calls the my_object.__getitem__(index) method behind the scenes. If you define this method in your own class, you make instances of your class "subscriptable," meaning they can be used with the [] operator.


How __getitem__ Works with Built-in Types

You don't need to define __getitem__ for Python's built-in types like lists, tuples, or dictionaries, but it's good to see how they use it.

# A simple list
my_list = ['apple', 'banana', 'cherry']
# This line...
print(my_list[1])
# ...is equivalent to calling this special method behind the scenes:
print(my_list.__getitem__(1))

Output:

Python getitem如何通过index实现索引访问?-图2
(图片来源网络,侵删)
banana
banana

The same principle applies to other sequences and mappings:

  • Tuples: my_tuple[0] calls my_tuple.__getitem__(0)
  • Strings: my_string[5] calls my_string.__getitem__(5)
  • Dictionaries: my_dict['key'] calls my_dict.__getitem__('key')

Creating Your Own Subscriptable Class with __getitem__

This is where __getitem__ becomes incredibly powerful. You can create custom objects that behave like lists, dictionaries, or any other data structure.

Example 1: A Custom List-like Sequence

Let's create a class that represents a sequence of even numbers.

class EvenNumbers:
    def __init__(self, limit):
        self.limit = limit
    def __getitem__(self, index):
        # We need to handle both integer indexing and slicing
        if isinstance(index, slice):
            # If it's a slice, return a new list of the sliced items
            start, stop, step = index.indices(self.limit)
            return [i for i in range(start, stop, step) if i % 2 == 0]
        elif isinstance(index, int):
            # If it's an integer, return the item at that "logical" position
            # The 0th even number is 0, the 1st is 2, the 2nd is 4, etc.
            if index < 0:
                # Handle negative indexing
                index += self.limit // 2 + (1 if self.limit % 2 != 0 else 0)
            if index < 0 or index >= self.limit // 2 + (1 if self.limit % 2 != 0 else 0):
                raise IndexError("EvenNumbers index out of range")
            return index * 2
        else:
            raise TypeError("Index must be an integer or a slice")
# --- Usage ---
evens = EvenNumbers(10) # Represents even numbers up to 10 (0, 2, 4, 6, 8)
# Accessing items by index
print(f"Item at index 0: {evens[0]}")      # Output: Item at index 0: 0
print(f"Item at index 1: {evens[1]}")      # Output: Item at index 1: 2
print(f"Item at index 4: {evens[4]}")      # Output: Item at index 4: 8
print(f"Item at index -1: {evens[-1]}")    # Output: Item at index -1: 8
# Using slicing
print(f"Slice [1:4]: {evens[1:4]}")        # Output: Slice [1:4]: [2, 4, 6]
# This will raise an IndexError
try:
    print(evens[5])
except IndexError as e:
    print(e) # Output: EvenNumbers index out of range

Example 2: A Custom Dictionary-like Object

Here's a class that maps day numbers (1-7) to their names.

Python getitem如何通过index实现索引访问?-图3
(图片来源网络,侵删)
class DayMap:
    def __init__(self):
        # Internal data store
        self._days = {
            1: "Monday",
            2: "Tuesday",
            3: "Wednesday",
            4: "Thursday",
            5: "Friday",
            6: "Saturday",
            7: "Sunday"
        }
    def __getitem__(self, key):
        # Simply delegate to the internal dictionary's __getitem__
        return self._days[key]
    def __setitem__(self, key, value):
        # You can also define __setitem__ to make your object mutable
        self._days[key] = value
# --- Usage ---
days = DayMap()
# Accessing an item
print(f"Day 3 is: {days[3]}") # Output: Day 3 is: Wednesday
# Modifying an item (because we defined __setitem__)
days[3] = "Hump Day"
print(f"Day 3 is now: {days[3]}") # Output: Day 3 is now: Hump Day

Advanced Indexing: Slicing with __getitem__

The index parameter in __getitem__ can be more than just an integer. It can also be a slice object (slice(start, stop, step)). This is how my_list[1:10:2] works.

Your __getitem__ method needs to check the type of the index argument to handle it correctly.

Here is a simplified example showing how to handle a slice:

class MySequence:
    def __init__(self, data):
        self.data = list(data)
    def __getitem__(self, index):
        print(f"__getitem__ called with index: {index!r} (type: {type(index).__name__})")
        if isinstance(index, slice):
            # The slice object itself has start, stop, and step attributes
            print(f"  -> Slice details: start={index.start}, stop={index.stop}, step={index.step}")
            # We can apply the slice directly to our internal data
            return self.data[index]
        elif isinstance(index, int):
            return self.data[index]
        else:
            raise TypeError("Invalid index type")
# --- Usage ---
seq = MySequence(range(10))
print("\nAccessing with an integer:")
item = seq[3] 
# Output:
# __getitem__ called with index: 3 (type: int)
# -> Accessing item at position 3
print("\nAccessing with a slice:")
sliced = seq[2:8:2]
# Output:
# __getitem__ called with index: slice(2, 8, 2) (type: slice)
#   -> Slice details: start=2, stop=8, step=2
# -> Returning [2, 4, 6]
print(f"\nResult of slice: {sliced}")

The NotImplementedError Alternative

For some objects, indexing might not make sense. Instead of raising a generic TypeError, it's more idiomatic to raise a NotImplementedError. This signals to the user (and other libraries) that the operation is intentionally not supported.

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __getitem__(self, index):
        # A point is not a sequence or mapping, so indexing is not supported.
        raise NotImplementedError("A Point object is not subscriptable.")
p = Point(10, 20)
try:
    value = p[0]
except NotImplementedError as e:
    print(e) # Output: A Point object is not subscriptable.

Summary

Concept Description Example
__getitem__(self, key) The special method that enables object[key] syntax. my_list[0]
key Parameter Can be an integer (for sequences), a string (for mappings), or a slice object. my_dict['name'], my_arr[1:5]
Why Override It? To create custom objects that behave like lists, dictionaries, or other data structures. class CustomList: ...
Companion Method __setitem__(self, key, value) enables assignment with object[key] = value. my_list[0] = 'new_value'
Companion Method __delitem__(self, key) enables deletion with del object[key]. del my_list[0]
When to Raise NotImplementedError When indexing is fundamentally not supported for your object's design. class Point: ...
分享:
扫描分享到社交APP
上一篇
下一篇