2015-03-04 21 views
9

Cách ghi đè __getattr__ bằng python 3 và kế thừa?đối tượng 'siêu' không có thuộc tính '__getattr__' trong python3

Khi tôi sử dụng như sau:

class MixinA: 
    def __getattr__(self, item): 
     # Process item and return value if known 
     if item == 'a': 
      return 'MixinA' 

     # If it is unknown, pass it along to give 
     # a chance to another class to handle it 
     return super().__getattr__(item) 

class MixinB: 
    def __getattr__(self, item): 
     # Process item and return value if known 
     if item == 'b': 
      return 'MixinB' 

     # If it is unknown, pass it along to give 
     # a chance to another class to handle it 
     return super().__getattr__(item) 

class Example(MixinA, MixinB): 
    # main class 
    pass 

tôi nhận được lỗi này.

>>> e = Example() 
>>> e.a 
'MixinA' 
>>> e.b 
'MixinB' 
>>> e.c 
--------------------------------------------------------------------------- 
AttributeError       Traceback (most recent call last) 
... 
AttributeError: 'super' object has no attribute '__getattr__' 

Tôi không thể lấy lỗi thuộc tính tham chiếu đến lớp và thuộc tính gốc? Đó là để nói:

AttributeError: 'Example' object has no attribute 'c' 

PS: Tôi thấy bài này 'super' object not calling __getattr__ nhưng tôi không chắc chắn để hiểu liệu có một giải pháp.

+0

Đây là trường hợp sử dụng con áp phích để sử dụng 'thuộc tính'. – dursk

+0

@mattm Ý của bạn là gì? Bạn có thể rõ ràng hơn không? Tôi không muốn sử dụng các thuộc tính. Hãy để tôi cố gắng giải thích thêm những gì tôi đang cố gắng đạt được. Tôi đang tạo một số mixin để tự động thêm các tùy chọn bổ sung vào một số thuộc tính dựa trên tên của chúng. Ví dụ: nếu một attr kết thúc bằng '_logs', thì bạn có thể lấy đoạn trích bằng cách thêm' __excerpt' => ví dụ: nếu obj có thuộc tính 'xxx_logs',' obj.xxx_logs__excerpt' sẽ bị chặn và sẽ hoạt động. – Michael

Trả lời

2

Nếu bạn muốn làm những thứ có các lớp mixin thì bạn cần tạo base-attribute-get-class luôn tăng một số AttributeError.

class AttrGetter: 
    def __getattr__(self, item): 
     raise AttributeError(item) 

class MixinA(AttrGetter): 
    def __getattr__(self, item): 
     if item == 'a': 
      return 'MixinA' 
     return super().__getattr__(item) 

class MixinB(AttrGetter): 
    def __getattr__(self, item): 
     if item == 'b': 
      return 'MixinB' 
     return super().__getattr__(item) 

class Example(MixinA, MixinB): 
    pass 

# mro stands for method resolution order. 
# It defines the order in which classes are searched for attributes. 
# We're looking for __getattr__ in this circumstance. 
print(Example.mro()) 
# [<class '__main__.Example'>, <class '__main__.MixinA'>, <class '__main__.MixinB'>, 
# <class '__main__.AttrGetter'>, <class 'object'>] 
# As you can see, searches for __getattr__ only check AttrGetter after having checked 
# both mixins first. 

e = Example() 
print(e.a) 
print(e.b) 
print(e.c) 

Tuy nhiên, đặc tính cho phép bạn làm điều này theo cách dễ dàng hơn và gọn gàng hơn.

class MixinA: 
    @property 
    def a(self): 
     return "MixinA" 

class MixinB: 
    @property 
    def b(self): 
     return "MixinB" 

class Example(MixinA, MixinB): 
    pass 

e = Example() 
print(e.a) 
print(e.b) 
print(e.c) 
4

Cơ chế truy xuất thuộc tính Python hoạt động theo cách mà lớp __getattr__ được gọi là "tài nguyên cuối cùng" để cố gắng lấy thuộc tính cho một thể hiện của lớp đó.

Mã của bạn đúng - không thành công do siêu lớp của bạn là "Ví dụ" - trong trường hợp này là "đối tượng" - không có thuộc tính __getattr__. Nếu bạn không ở cấp phân cấp sâu và muốn thực hiện truy xuất thuộc tính tùy chỉnh cho một lớp kế thừa trực tiếp từ đối tượng (trong Py3 có thể được biểu thị là không có cơ sở nào như trường hợp trong mã của bạn) - chỉ cần nâng cao "AttributeError "nếu tra cứu tùy chỉnh của bạn không thành công.

Nếu ý định của bạn là, thay vào đó, tìm kiếm tùy chỉnh yur có ưu tiên hơn cơ chế truy xuất thuộc tính bình thường của Python (và không được gọi là dự phòng), bạn nên triển khai __getattribute__ thay vì __getattr__.

Trong trường hợp này, lớp cơ sở - đối tượng - có phương thức __getattribute__ mà bạn phải gọi để truy xuất thuộc tính thông thường - vấn đề là bạn phải gọi cho mọi thứ bạn muốn - bao gồm tên phương thức và thuộc tính bạn đã đặt. tức là .: cái gì đó dọc:

class Example: 

    def __init__(self): 
     self.attrs_to_override = ["attr1", "foo", "bar"] 

    def docustomstuff(self, attr): 
     ... 

    def __getattribute__(self, attr): 
     getter = super().__getattribute__ 
     if attr in getter("attrs_to_override"): 
      getter("docustomstuff")(attr) 
     return getter(attr) 

Nói tóm lại, nếu bạn nghĩ rằng bạn nên thực hiện __getattribute__ thay vì __getattr__ có lẽ bạn đang cố gắng tiếp cận sai lầm, trừ những trường hợp rất cụ thể. Những gì bạn có thể không biết đến ở đây là: __getattr__ sẽ không được gọi nếu một thuộc tính bình thường bởi tên mong muốn đã không tồn tại, vì vậy không cần phải gọi nó trong lớp cha (trừ khi siêu lớp trong trường hợp là không phản đối và có một tùy biến được biết đến với nó)

chỉnh sửa

Ngoài ra, chỉ cần kiểm tra xem lớp cha tới làm có __getattr__ theo cách đơn giản nhất:

... 
if hasattr(super(), "__getattr__"): 
    return super().__getattr__(attr) 
raise AttributeError 
+0

Cảm ơn bạn đã phản hồi. Có, tôi nghĩ rằng tôi hiểu sự khác biệt giữa '__getattr__' và' __getattribute__' và tôi muốn sử dụng '__getattr__'. Tôi không muốn nâng cao 'AttributeError' vì tôi muốn có một số mixin thực hiện phương thức' __getattr__' và tôi muốn tất cả chúng có cơ hội để chặn nó. Tôi đã chỉnh sửa bài đăng của mình để hiển thị ví dụ này. Cho tôi biết bạn nghĩ gì. Về cơ bản tôi đã có được 'AttributeError' nhưng tôi muốn nó nằm ở cấp độ cao và thuộc tính thay vì' super'. Bất kỳ ý tưởng? – Michael

+0

Ở đó - tôi đã thêm một ví dụ về cách thực hiện nó bằng '__getattr__' – jsbueno

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