Làm cách nào để ghi đè __getattr__ trong Python mà không phá vỡ hành vi mặc định?


185

Tôi muốn ghi đè __getattr__phương thức trên một lớp để làm một cái gì đó lạ mắt nhưng tôi không muốn phá vỡ hành vi mặc định.

Cách chính xác để làm điều này là gì?


20
"Phá vỡ", nó yêu cầu, không "thay đổi." Điều đó đủ rõ ràng: các thuộc tính "ưa thích" không nên can thiệp vào các thuộc tính tích hợp và nên hành xử nhiều nhất có thể như chúng. Câu trả lời của Michael vừa chính xác vừa hữu ích.
olooney

Câu trả lời:


267

Ghi đè __getattr__sẽ ổn - __getattr__chỉ được gọi là giải pháp cuối cùng, tức là nếu không có thuộc tính nào trong trường hợp khớp với tên. Chẳng hạn, nếu bạn truy cập foo.bar, thì __getattr__sẽ chỉ được gọi nếu fookhông có thuộc tính được gọi bar. Nếu thuộc tính là thuộc tính bạn không muốn xử lý, hãy nâng cao AttributeError:

class Foo(object):
    def __getattr__(self, name):
        if some_predicate(name):
            # ...
        else:
            # Default behaviour
            raise AttributeError

Tuy nhiên, không giống như __getattr__, __getattribute__sẽ được gọi đầu tiên (chỉ hoạt động cho các lớp kiểu mới, tức là những lớp kế thừa từ đối tượng). Trong trường hợp này, bạn có thể duy trì hành vi mặc định như vậy:

class Foo(object):
    def __getattribute__(self, name):
        if some_predicate(name):
            # ...
        else:
            # Default behaviour
            return object.__getattribute__(self, name)

Xem tài liệu Python để biết thêm .


Bah, chỉnh sửa của bạn có cùng một điều tôi đã thể hiện trong câu trả lời của mình, +1.

12
Thật tuyệt, Python dường như không thích gọi siêu nhân __getattr__- bạn có biết phải làm gì không? ( AttributeError: 'super' object has no attribute '__getattr__')
gatoatigrado

1
Không nhìn thấy mã của bạn, thật khó để nói, nhưng có vẻ như không ai trong số các siêu lớp của bạn xác định getattr .
Colin vH

Điều này cũng hoạt động với hasattr bởi vì: "Điều này được thực hiện bằng cách gọi getattr (đối tượng, tên) và xem liệu nó có làm tăng ngoại lệ hay không." docs.python.org/2/library/functions.html#hasattr
Shabang

1
-1 Điều này không sửa đổi hành vi mặc định. Bây giờ bạn có một AttributeErrorkhông có bối cảnh của thuộc tính trong ngoại lệ args.
wim

33
class A(object):
    def __init__(self):
        self.a = 42

    def __getattr__(self, attr):
        if attr in ["b", "c"]:
            return 42
        raise AttributeError("%r object has no attribute %r" %
                             (self.__class__.__name__, attr))

>>> a = A()
>>> a.a
42
>>> a.b
42
>>> a.missing
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in __getattr__
AttributeError: 'A' object has no attribute 'missing'
>>> hasattr(a, "b")
True
>>> hasattr(a, "missing")
False

Cảm ơn vì điều đó. Chỉ muốn chắc chắn rằng tôi đã có thông báo mặc định chính xác mà không cần đào sâu trong nguồn.
ShawnFumo

4
Tôi nghĩ rằng nó self.__class__.__name__nên được sử dụng thay vì self.__class__trong trường hợp lớp ghi đè__repr__
Michael Scott Cuthbert

3
Điều này tốt hơn câu trả lời được chấp nhận, nhưng sẽ tốt hơn nếu không phải viết lại mã đó và có khả năng bỏ lỡ các thay đổi ngược dòng nếu từ ngữ được sửa đổi hoặc thêm ngữ cảnh vào đối tượng ngoại lệ trong tương lai.
wim

12

Để mở rộng câu trả lời của Michael, nếu bạn muốn duy trì hành vi mặc định bằng cách sử dụng __getattr__, bạn có thể làm như vậy:

class Foo(object):
    def __getattr__(self, name):
        if name == 'something':
            return 42

        # Default behaviour
        return self.__getattribute__(name)

Bây giờ thông báo ngoại lệ là mô tả nhiều hơn:

>>> foo.something
42
>>> foo.error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in __getattr__
AttributeError: 'Foo' object has no attribute 'error'

2
@ fed.pavlo bạn có chắc không? Có lẽ bạn pha trộn __getattr____getattribute__?
Jose Luis

lỗi của tôi. Tôi đã bỏ lỡ cuộc gọi đến phương pháp khác nhau từ bản thân. ; (
fed.pavlo

1
Thật vậy, câu trả lời của @ Michael không đầy đủ nếu không có câu trả lời này
Grijesh Chauhan
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.