Search code examples
pythonweak-referencesiterable

Python weakref.proxy object considered iterable?


I came across the following (using Python 3.8.3):

from collections.abc import Iterable
from weakref import proxy

class Dummy:
   pass

isinstance(d := Dummy, Iterable)
# False (as expected)

isinstance(p := proxy(d), Iterable)
# True (why?!)

for _ in p:
    # raises TypeError
    pass

How could the proxy object pass the iterable-test?


Solution

  • It provides __iter__ in case the underlying type provides it. __iter__ must be implemented on the type to work, not the instance, so it can't conditionally define __iter__ without fragmenting into many different classes, not just a single proxy class.

    Unfortunately, the Iterable test just checks if the class defines __iter__, not whether it works, and proxy doesn't know if it really works without calling the wrapped class's __iter__. If you want to check for iterability, just iterate it, and catch the TypeError if it fails. If you can't iterate it immediately, while it's subject to time-of-check/time-of-use race conditions, you could write your own simple tester that covers whether it can actually be iterated:

    def is_iterable(maybeiter):
        try:
            iter(maybeiter)
        except TypeError:
            return False
        else:
            return True