Hiểu phương pháp __getitem__


137

Tôi đã trải qua hầu hết các tài liệu về __getitem__ các tài liệu Python, nhưng tôi vẫn không thể nắm bắt được ý nghĩa của nó.

Vì vậy, tất cả những gì tôi có thể hiểu là __getitem__được sử dụng để thực hiện các cuộc gọi như thế nào self[key]. Nhưng công dụng của nó là gì?

Hãy nói rằng tôi có một lớp python được định nghĩa theo cách này:

class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def __getitem__(self,key):
        print ("Inside `__getitem__` method!")
        return getattr(self,key)

p = Person("Subhayan",32)
print (p["age"])

Điều này trả về kết quả như mong đợi. Nhưng tại sao lại sử dụng __getitem__ngay từ đầu? Tôi cũng đã nghe rằng Python gọi __getitem__nội bộ. Nhưng tại sao nó làm điều đó?

Ai đó có thể vui lòng giải thích điều này chi tiết hơn?


Điều này có thể được quan tâm cho một ví dụ sử dụng: Cách phân lớp chính xác và ghi đè getitem & setitem
roganjosh

4
Việc __getitem__sử dụng trong ví dụ của bạn không có nhiều ý nghĩa, nhưng hãy tưởng tượng rằng bạn cần phải viết một lớp giống như từ điển hoặc danh sách tùy chỉnh, phải hoạt động với mã hiện có sử dụng []. Đó là một tình huống có __getitem__ích.
Pieter Witvoet

Câu trả lời:


157

Cong Ma làm tốt công việc giải thích những gì __getitem__ được sử dụng cho - nhưng tôi muốn cho bạn một ví dụ có thể hữu ích. Hãy tưởng tượng một lớp học mô hình một tòa nhà. Trong dữ liệu của tòa nhà, nó bao gồm một số thuộc tính, bao gồm các mô tả về các công ty chiếm giữ mỗi tầng:

Không sử dụng __getitem__chúng ta sẽ có một lớp như thế này:

class Building(object):
     def __init__(self, floors):
         self._floors = [None]*floors
     def occupy(self, floor_number, data):
          self._floors[floor_number] = data
     def get_floor_data(self, floor_number):
          return self._floors[floor_number]

building1 = Building(4) # Construct a building with 4 floors
building1.occupy(0, 'Reception')
building1.occupy(1, 'ABC Corp')
building1.occupy(2, 'DEF Inc')
print( building1.get_floor_data(2) )

Tuy nhiên, chúng tôi có thể sử dụng __getitem__(và đối tác của nó __setitem__) để sử dụng lớp Xây dựng 'đẹp hơn'.

class Building(object):
     def __init__(self, floors):
         self._floors = [None]*floors
     def __setitem__(self, floor_number, data):
          self._floors[floor_number] = data
     def __getitem__(self, floor_number):
          return self._floors[floor_number]

building1 = Building(4) # Construct a building with 4 floors
building1[0] = 'Reception'
building1[1] = 'ABC Corp'
building1[2] = 'DEF Inc'
print( building1[2] )

Việc bạn sử dụng __setitem__như thế này có thực sự phụ thuộc vào cách bạn dự định trừu tượng hóa dữ liệu của mình hay không - trong trường hợp này, chúng tôi đã quyết định coi tòa nhà là nơi chứa các tầng (và bạn cũng có thể triển khai Trình lặp cho Tòa nhà và thậm chí có thể cắt lát - tức là nhận được nhiều dữ liệu của một tầng cùng một lúc - nó phụ thuộc vào những gì bạn cần.


15
Chỉ để chia sẻ một cái gì đó tôi đã học được chỉ sau khi đọc câu trả lời nhiều lần: một khi bạn có một getitem, bạn không cần phải gọi hàm đó một cách rõ ràng. Khi anh ta gọi building1[2]nó mà gọi chính nó trong nội bộ gọi getitem. Vì vậy, điểm @ tony-acholk-66 đang tạo ra là, bất kỳ thuộc tính / biến nào của lớp có thể được truy xuất trong thời gian chạy bằng cách chỉ cần gọi tên đối tượng [matrixblename]. Chỉ cần làm rõ điều này vì ban đầu tôi không rõ ràng và viết nó ở đây với hy vọng nó sẽ giúp được ai đó. Xóa nếu dư thừa xin vui lòng
mithunpaul

3
@mithunpaul ký hiệu [index] không được sử dụng để lấy thuộc tính / biến / thuộc tính của một lớp - nó đang tìm kiếm trên một đối tượng container - ví dụ như lấy một đối tượng con từ cha mẹ mà cha mẹ duy trì một danh sách các con của nó. Trong ví dụ của tôi - lớp Building là một container (trong trường hợp này là tên Tầng), nhưng nó có thể là lớp container cho các lớp Tầng.
Tony Suffolk 66

Ngoại trừ việc nó sẽ không hỗ trợ len()và bạn sẽ nhận được TypeError:TypeError: object of type 'Building' has no len()
Ciasto piekarz

Hỗ trợ len (và các tính năng khác như lặp, v.v.) không phải là mục đích của ví dụ của tôi. Việc thực hiện một phương thức dunder_len là không quan trọng.
Tony Suffolk 66

@ TonySuffolk66: điều này có đúng không khi ____len__ xác định lần lặp cho chỉ số (sàn) trong ví dụ của bạn về vòng lặp ____getitem__ nào?
Alex

72

Các []cú pháp để nhận mục bằng phím hoặc chỉ số chỉ là đường cú pháp.

Khi bạn đánh giá a[i]các cuộc gọi Python a.__getitem__(i)(hoặc type(a).__getitem__(a, i), nhưng sự khác biệt này là về các mô hình thừa kế và không quan trọng ở đây). Ngay cả khi lớp của acó thể không xác định rõ ràng phương thức này, nó thường được kế thừa từ một lớp tổ tiên.

Tất cả các tên phương thức đặc biệt (Python 2.7) và ngữ nghĩa của chúng được liệt kê ở đây: https://docs.python.org/2.7/reference/datamodel.html#special-method-names


8

Phương thức ma thuật __getitem__về cơ bản được sử dụng để truy cập các mục danh sách, mục từ điển, phần tử mảng, v.v ... Nó rất hữu ích để tra cứu nhanh các thuộc tính thể hiện.

Ở đây tôi đang trình bày điều này với một lớp người mẫu có thể được khởi tạo bằng 'tên', 'tuổi' và 'dob' (ngày sinh). Các __getitem__phương pháp được viết theo một cách mà người ta có thể truy cập vào các thuộc tính dụ được lập chỉ mục, chẳng hạn như tên đầu tiên hoặc cuối cùng, ngày, tháng hoặc năm của DOB vv

import copy

# Constants that can be used to index date of birth's Date-Month-Year
D = 0; M = 1; Y = -1

class Person(object):
    def __init__(self, name, age, dob):
        self.name = name
        self.age = age
        self.dob = dob

    def __getitem__(self, indx):
        print ("Calling __getitem__")
        p = copy.copy(self)

        p.name = p.name.split(" ")[indx]
        p.dob = p.dob[indx] # or, p.dob = p.dob.__getitem__(indx)
        return p

Giả sử một đầu vào của người dùng như sau:

p = Person(name = 'Jonab Gutu', age = 20, dob=(1, 1, 1999))

Với sự trợ giúp của __getitem__phương thức, người dùng có thể truy cập các thuộc tính được lập chỉ mục. ví dụ,

print p[0].name # print first (or last) name
print p[Y].dob  # print (Date or Month or ) Year of the 'date of birth'

Ví dụ tuyệt vời! Tôi đã tìm kiếm khắp nơi về cách triển khai getitem khi có nhiều tham số trong init và tôi đang vật lộn để tìm một triển khai đúng và cuối cùng đã thấy điều này! Ủng hộ và cảm ơn bạn!
Rahul P
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.