Why is __getattr__ not working with __exit__?

I came across this as a surprise, trying to work out another question .

It seemed extremely strange to me, I thought it was worth asking a question. Why __getattr__ n't __getattr__ work with ?

if I create this object:

 class FileHolder(object): def __init__(self,*args,**kwargs): self.f= file(*args,**kwargs) def __getattr__(self,item): return getattr(self.f,item) 

and using it with with ,

 >>> a= FileHolder("a","w") >>> a.write <built-in method write of file object at 0x018D75F8> >>> with a as f: ... print f ... Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: __exit__ >>> a.__exit__ <built-in method __exit__ of file object at 0x018D75F8> 

Why is this happening?

EDIT

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

It definitely does not inherit __exit__

+4
source share
2 answers

I cannot say for sure, but after reading the PEP describing the with statement:

http://www.python.org/dev/peps/pep-0343/

It jumped on me:

 A new statement is proposed with the syntax: with EXPR as VAR: BLOCK .... The translation of the above statement is: mgr = (EXPR) exit = type(mgr).__exit__ # Not calling it yet value = type(mgr).__enter__(mgr) .... 

Right there. The with statement does not call __getattr__(__exit__) , but calls type(a).__exit__ , which does not exist, indicating an error.

Therefore, you just need to define them:

 class FileHolder(object): def __init__(self,*args,**kwargs): self.f= file(*args,**kwargs) def __enter__(self,*args,**kwargs): return self.f.__enter__(*args,**kwargs) def __exit__(self,*args,**kwargs): self.f.__exit__(*args,**kwargs) def __getattr__(self,item): return getattr(self.f,item) 
+5
source

The statement with SETUP_WITH operation SETUP_WITH for __exit__ as a "special method search" that ignores __getattr__ and __getattribute__ in new-style classes (but not in old-style classes). See this mailing list thread for more information, where they discuss adding the semantics of finding special methods to with (which they ultimately do). See Also, a special search for methods for new-style classes for a detailed discussion of why these special methods are viewed in this way.

In particular, a special method search also bypasses __getattr__ in the type object. So, despite the fact that the documentation states that the method is considered as type(mgr).__exit__ , this code does not work:

 class M(type): def __getattr__(*args): return lambda: 0 class X(object): __metaclass__ = M x = X() type(x).__exit__ # works, returns a lambda with x: pass # fails, AttributeError 
+5
source

Source: https://habr.com/ru/post/1436733/


All Articles