2012-05-29 21 views
7

Tuyên bố từ chối Đây chỉ là một bài tập trong lập trình meta, nó không có mục đích thực tế .Xác định __getattr__ và __getitem__ trên một hàm không có hiệu lực

tôi đã gán __getitem____getattr__ phương pháp trên một đối tượng chức năng, nhưng không có hiệu lực thi hành ...

def foo(): 
    print "foo!" 

foo.__getitem__ = lambda name: name 
foo.__getattr__ = lambda name: name 
foo.baz = 'baz' 

Sanity kiểm tra xem chúng tôi có thể gán thuộc tính cho một chức năng:

>>> foo.baz 
'baz' 

Gọn gàng. Làm thế nào về "getters ma thuật"?

>>> foo.bar 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'function' object has no attribute 'bar' 

>>> foo['foo'] 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: 'function' object is not subscriptable 

>>> getattr(foo, 'bar') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'function' object has no attribute 'bar' 

Có thể có "ma thuật getter" trên đối tượng chức năng không?

Trả lời

6

Không! Gán __getitem__ đến một thể hiện không hoạt động trên bất kỳ loại của đối tượng:

>>> class A(object): 
... pass 
... 
>>> a = A() 
>>> a.__getattr__ = lambda name: name 
>>> a.foo 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'A' object has no attribute 'foo' 

bạn không thể xác định __getattr__ trên được xây dựng trong loại chức năng:

>>> import types 
>>> types.FunctionType.__getitem__ = lambda name: name 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: can't set attributes of built-in/extension type 'function' 

bạn không thể phân loại types.FunctionType:

>>> import types 
>>> class F(types.FunctionType): 
... pass 
... 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: Error when calling the metaclass bases 
type 'function' is not an acceptable base type 
+2

@kindall chỉ ra điều này bên dưới, nhưng tôi sẽ đề cập đến nó ở đây để nó ở lại với câu trả lời. các lớp kiểu mới không sử dụng độ phân giải phương thức "bình thường" với '__getattr__' và bạn bè của nó. 'a.foo' được dịch nội bộ thành' type (a) .__ getattr __ ('foo') '. Vì vậy, thuộc tính '__getattr__' được lưu trữ trong' a .__ dict__' không được tìm thấy. Hy vọng rằng có ý nghĩa ...: -/ – colinta

1

AHHA! Sử dụng __call__, và quấn chức năng trong F()

class F(object): 
    def __init__(self, fn): 
     self.__dict__['fn'] = fn 

    def __call__(self, *args, **kwargs): 
     return self.fn(*args, **kwargs) 

    def __getitem__(self, name): 
     return name 

    def __getattr__(self, name): 
     return name 

>>> foo = F(foo) 
>>> f.bar 
'bar' 
>>> f['foo'] 
'foo' 
>>> foo() 
foo! 
+2

Về cơ bản mô tả trang trí của bạn :) –

+1

Yup, và tôi chơi với điều này như một trang trí, nhưng những điều thú vị phá vỡ ... nếu tôi trang trí một phương pháp với một lớp học, khi gọi phương pháp nó không trở thành một phương thức bị ràng buộc. do đó, một "class-as-decorator" dường như không phải là "điều tương tự" như là một "function-as-decorator". Chúng ta sẽ thấy, tôi sẽ đăng những phát hiện của tôi ở đây. – colinta

+0

Cả hai đều giống hệt nhau về những gì chúng cung cấp, cả hai đều trả về một đối tượng 'có thể gọi được '. Nó chỉ là lớp học như trang trí là neater và sạch hơn –

2

Ít nhất là trên các lớp học kiểu mới (đó là những loại chỉ bằng Python 3 và loại bạn nên sử dụng bằng Python 2), Python chỉ tìm kiếm phương pháp kỳ diệu trên lớp (và tổ tiên của nó), không bao giờ trên ví dụ. Docs here.

Và tất nhiên bạn không thể sửa đổi loại chức năng hoặc lấy được từ nó. Tuy nhiên, như bạn đã tìm thấy, bất kỳ lớp nào có phương thức __call__() sẽ tạo ra các cá thể có thể gọi được, vì vậy đó là cách để thực hiện nó.

+0

Tất cả sự thật - tôi muốn chia sẻ thêm một chút về câu chuyện đầy đủ để bất cứ ai vấp ngã ở đây sẽ không phải đi quanh các tài liệu để tìm ra thứ này. nhưng yeah, các lớp kiểu mới gọi là 'type (obj) .__ getattr__'. Đó là tài liệu, chắc chắn, nhưng phản trực giác với tôi ... tại sao không chỉ sử dụng độ phân giải phương pháp bình thường? – colinta

+0

"Lý do đằng sau hành vi này nằm ở một số phương pháp đặc biệt như' __hash __() 'và' __repr __() 'được thực hiện bởi tất cả các đối tượng, bao gồm các đối tượng kiểu. , chúng sẽ thất bại khi được gọi trên chính đối tượng kiểu. " – kindall

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