__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 a
thuộ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 == f
và 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ì
f
có một a
thuộ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 self
và 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.