2012-11-24 33 views
17

Phương pháp __getattribute__ cần được viết cẩn thận để tránh vòng lặp vô hạn. Ví dụ:Python: tránh các vòng vô hạn trong __getattribute__

class A: 
    def __init__(self): 
     self.x = 100 

    def __getattribute__(self, x): 
     return self.x 

>>> a = A() 
>>> a.x # infinite looop 
RuntimeError: maximum recursion depth exceeded while calling a Python object 


class B: 
    def __init__(self): 
     self.x = 100 

    def __getattribute__(self, x): 
     return self.__dict__[x] 

>>> b = B() 
>>> b.x # infinite looop 
RuntimeError: maximum recursion depth exceeded while calling a Python object 

Do đó chúng ta cần phải viết phương pháp theo cách này:

class C: 
    def __init__(self): 
     self.x = 100 

    def __getattribute__(self, x): 
     # 1. error 
     # AttributeError: type object 'object' has no attribute '__getattr__' 
     # return object.__getattr__(self, x) 

     # 2. works 
     return object.__getattribute__(self, x) 

     # 3. works too 
     # return super().__getattribute__(x) 

Câu hỏi của tôi là tại sao object.__getattribute__ phương pháp làm việc? Từ đâu object nhận phương thức __getattribute__? Và nếu object không có bất kỳ __getattribute__, thì chúng tôi chỉ đang gọi cùng một phương thức trên lớp C nhưng thông qua siêu lớp. Tại sao, sau đó gọi phương thức thông qua siêu lớp không dẫn đến một vòng lặp vô hạn?

+2

Bạn có chắc là bạn cần '__getattribute__' chứ không phải' __getattr__'? –

+4

Có, vì tôi cần phải đánh chặn TẤT CẢ thuộc tính tìm nạp trong lớp của tôi. Nhưng ngay cả khi tôi không làm vậy, tôi vẫn muốn biết chi tiết đầy đủ về lý do tại sao điều này là như vậy. – treecoder

+1

Vâng, bạn phải chặn * tất cả * quyền truy cập thuộc tính hoặc bạn không :-) –

Trả lời

21

Có vẻ như bạn đang thực hiện __getattribute__ chỉ là một móc, nếu bạn cung cấp thì Python sẽ gọi nó, và nếu không thì thông dịch viên sẽ làm phép thuật bình thường trực tiếp.

Điều đó không đúng. Khi python tra cứu các thuộc tính trên các cá thể, __getattribute__ là mục nhập chính cho tất cả quyền truy cập thuộc tính và object cung cấp việc triển khai mặc định. Do đó, việc triển khai của bạn là ghi đè bản gốc và nếu việc triển khai của bạn không cung cấp phương tiện trả lại thuộc tính thay thế nào khác. Bạn không thể sử dụng quyền truy cập thuộc tính trong phương thức đó, vì tất cả quyền truy cập thuộc tính vào cá thể (self) được chuyển một lần nữa thông qua type(self).__getattribute__(self, attr).

Cách tốt nhất xung quanh việc này là bằng cách gọi lại bản gốc đã ghi đè. Đó là nơi super(C, self).__getattribute__(attr) xuất hiện; bạn đang yêu cầu lớp tiếp theo theo thứ tự lớp học để xử lý quyền truy cập thuộc tính cho bạn.

Ngoài ra, bạn có thể gọi trực tiếp phương thức không giới hạn object.__getattribute__(). Việc thực hiện C của phương pháp này là điểm dừng cuối cùng để truy cập thuộc tính (nó có quyền truy cập trực tiếp đến __dict__ và do đó không bị ràng buộc với cùng một giới hạn).

Lưu ý rằng super() trả về một đối tượng proxy sẽ tìm kiếm bất kỳ phương thức nào có thể được tìm thấy tiếp theo trong các lớp cơ sở được sắp xếp theo phương thức phân giải phương pháp. Nếu không tồn tại phương thức như vậy, nó sẽ thất bại với lỗi thuộc tính. Nó sẽ không bao giờ gọi phương thức gốc. Do đó, Foo.bar() tra cứu super(Foo, self).bar sẽ là triển khai cấp cơ sở hoặc lỗi thuộc tính, không bao giờ tự mình làFoo.bar.

8

Khi bạn làm điều này:

return object.__getattribute__(self, x) 

bạn đang gọi một chức năng cụ thể - một trong những quy định tại các lớp đối tượng, và không phải là một quy định tại A, vì vậy không có đệ quy.

Khi bạn làm điều này: python

return self.x 

bạn được cho phép chọn chức năng để gọi, và nó gọi một từ A, và bạn có một đệ quy vô hạn.

+0

Không có hàm nào được định nghĩa trên lớp' object'. Nếu có một hàm được định nghĩa ở đó, thì 'đối tượng' cũng sẽ định nghĩa' __getattr__' (trong đó, như lỗi # 1 cho thấy nó không có). Vậy thì tại sao đối tượng sẽ xác định phương thức '__getattribute__'? – treecoder

+1

@greengit: tại sao không? Tôi thấy '' khi tôi truy cập 'đối tượng .__ getattribute__'. –

+0

Vì vậy, bạn đang nói 'đối tượng' chỉ định nghĩa' __getattribte__' và NOT '__getattr__'? Theo như tôi biết, việc thực thi lớp 'object' của tất cả các phương thức này là no-op. Đúng nếu tôi sai ở đâu đó. – treecoder

2

Viết nó như thế này (C thừa hưởng từ đối tượng):

class C(object): 
    def __init__(self): 
     self.x = 100 

    def __getattribute__(self, x): 
     return object.__getattribute__(self, x) 

Bây giờ bạn thấy tại sao đối tượng .__ getAttribute __ (tự, x) hoạt động - bạn đang gọi đối tượng cha.

Các vấn đề liên quan