__getattribute__ được gọi bất cứ khi nào một truy cập thuộc tính xảy ra.
class Foo(object):
def __init__(self, a):
self.a = 1
def __getattribute__(self, attr):
try:
return self.__dict__[attr]
except KeyError:
return 'default'
f = Foo(1)
f.a
Điều này sẽ gây ra đệ quy vô hạn. Thủ phạm ở đây là đường dây return self.__dict__[attr]. Hãy giả vờ (Nó đủ gần với sự thật) rằng tất cả các thuộc tính được lưu trữ self.__dict__và có sẵn theo tên của chúng. Dòng
f.a
cố gắng truy cập athuộc tính của f. Cuộc gọi này f.__getattribute__('a'). __getattribute__sau đó cố gắng tải self.__dict__. __dict__là một thuộc tính của self == fvà vì vậy các cuộc gọi python f.__getattribute__('__dict__')mà một lần nữa cố gắng truy cập vào thuộc tính '__dict__'. Đây là đệ quy vô hạn.
Nếu __getattr__đã được sử dụng thay thế thì
- Nó sẽ không bao giờ chạy vì
fcó một athuộc tính.
- Nếu nó đã chạy, (giả sử bạn đã yêu cầu
f.b) thì nó sẽ không được gọi để tìm __dict__vì nó đã ở đó và __getattr__chỉ được gọi nếu tất cả các phương pháp tìm thuộc tính khác không thành công .
Cách 'chính xác' để viết lớp trên bằng cách sử dụng __getattribute__là
class Foo(object):
# Same __init__
def __getattribute__(self, attr):
return super(Foo, self).__getattribute__(attr)
super(Foo, self).__getattribute__(attr)liên kết __getattribute__phương thức của siêu lớp 'gần nhất' (chính thức, lớp tiếp theo trong Thứ tự giải quyết phương thức của lớp hoặc MRO) với đối tượng hiện tại selfvà sau đó gọi nó và cho phép thực hiện công việc.
Tất cả những rắc rối này đều được tránh bằng cách sử dụng __getattr__cho phép Python làm điều bình thường cho đến khi không tìm thấy thuộc tính. Tại thời điểm đó, Python điều khiển __getattr__phương thức của bạn và cho phép nó đưa ra một cái gì đó.
Cũng đáng lưu ý rằng bạn có thể chạy theo đệ quy vô hạn __getattr__.
class Foo(object):
def __getattr__(self, attr):
return self.attr
Tôi sẽ để nó như một bài tập.