Question regarding unexpected behavior in using __enter__ method

Peter Otten __peter__ at web.de
Fri Apr 21 03:41:07 EDT 2023


On 21/04/2023 00:44, Lorenzo Catoni wrote:
> Dear Python Mailing List members,
>
> I am writing to seek your assistance in understanding an unexpected
> behavior that I encountered while using the __enter__ method. I have
> provided a code snippet below to illustrate the problem:
>
> ```
>>>> class X:
> ...     __enter__ = int
> ...     __exit__ = lambda *_: None
> ...
>>>> with X() as x:
> ...     pass
> ...
>>>> x
> 0
> ```
> As you can see, the __enter__ method does not throw any exceptions and
> returns the output of "int()" correctly. However, one would normally expect
> the input parameter "self" to be passed to the function.
>
> On the other hand, when I implemented a custom function in place of the
> __enter__ method, I encountered the following TypeError:
>
> ```
>>>> def myint(*a, **kw):
> ...     return int(*a, **kw)
> ...
>>>> class X:
> ...     __enter__ = myint
> ...     __exit__ = lambda *_: None
> ...
>>>> with X() as x:
> ...     pass
> ...
> Traceback (most recent call last):
>    File "<stdin>", line 1, in <module>
>    File "<stdin>", line 2, in myint
> TypeError: int() argument must be a string, a bytes-like object or a real
> number, not 'X'
> ```
> Here, the TypeError occurred because "self" was passed as an input
> parameter to "myint". Can someone explain why this unexpected behavior
> occurs only in the latter case?

Cameron is right, it's the descriptor protocol. Technically

inst.attr

invokes attr.__get__(...) if it exists:

 >>> class A:
	def __get__(self, *args): return args


 >>> class B: pass

 >>> class X:
	a = A()
	b = B()


 >>> x = X()
 >>> x.b
<__main__.B object at 0x02C2E388>
 >>> x.a
(<__main__.X object at 0x02C2E280>, <class '__main__.X'>)

Python functions support the descriptor protocol

 >>> hasattr(lambda: None, "__get__")
True

while builtin functions don't:

 >>> hasattr(ord, "__get__")
False


I'm unsure whether to regard int as a class or or function, but as there
is no __get__

 >>> hasattr(int, "__get__")
False

it behaves like builtin functions in this case.



More information about the Python-list mailing list