I came across this as a bit of a surprise while trying to work out another question.
This seemed extremely odd to me, I thought it was worth asking the question. Why doesn't __getattr__
appear to work with with
?
if I make 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 does this happen?
>>> object.__exit__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'object' has no attribute '__exit__'
It definitely isn't inheriting __exit__
I can't say for sure, but after reading over the PEP describing the with statement:
http://www.python.org/dev/peps/pep-0343/
This jumped out at 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 giving the error.
So you just need to define those:
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)