Python: LINQ capable class supporting simultaneously laziness and fluent design
<p>How to create an Python class which would be a iterable wrapper with LINQ-like methods (select, where, orderby, etc.) without using extension methods or monkey patching.
?</p>

<p>That is this LinqCapable class would be capable to return its own type when it is relevant (i.e. fluent design) and support lazy evaluation.</p>

<p>I'm just looking here for a snippet as a starting point.</p>

<p>It's not enough to just return a generator for the implemented linq methods, you need to have it return an instance of the wrapper to be able to chain additional calls.</p>

<p>You can create a metaclass which can rewrap the linq implementations.  So with this, you can just implement the methods you want to support and use some special decorators to ensure it remains chainable.</p>

<pre><code>def linq(iterable):
    from functools import wraps
    def as_enumerable(f):
        f._enumerable = True
        return f
    class EnumerableMeta(type):
        def __new__(metacls, name, bases, namespace):
            cls = type.__new__(metacls, name, bases, namespace)
            def to_enumerable(f):
                @wraps(f)
                def _f(self, *args, **kwargs):
                    return cls(lambda: f(self, *args, **kwargs))
                return _f
            for n, f in namespace.items():
                if hasattr(f, '_enumerable'):
                    setattr(cls, n, to_enumerable(f))
            return cls
    class Enumerable(metaclass=EnumerableMeta):
        def __init__(self, _iterable):
            self._iterable = _iterable
        def __iter__(self):
            return iter(self._iterable())
        @as_enumerable
        def intersect(self, second):
            yield from set(self._iterable()).intersection(second)
        @as_enumerable
        def select(self, selector):
            yield from map(selector, self._iterable())
        @as_enumerable
        def union(self, second):
            yield from set(self._iterable()).union(second)
        @as_enumerable
        def where(self, predicate):
            yield from filter(predicate, self._iterable())
        @as_enumerable
        def skip(self, count):
            yield from (x for x, i in enumerate(self._iterable()) if i &gt;= count)
        @as_enumerable
        def skip_while(self, predicate):
            it = iter(self._iterable())
            for x in it:
                if not predicate(x):
                    yield x
                    break
            yield from it
        @as_enumerable
        def take(self, count):
            yield from (x for x, i in enumerate(self._iterable()) if i &lt; count)
        @as_enumerable
        def take_while(self, predicate):
            for x in self._iterable():
                if not predicate(x): break
                yield x
        @as_enumerable
        def zip(self, second, result_selector=lambda a, b: (a, b)):
            yield from map(lambda x: result_selector(*x), zip(self._iterable(), second))
        def single(self, predicate=lambda _: True):
            has_result = False
            for x in self._iterable():
                if predicate(x):
                    if has_result:
                        raise TypeError('sequence contains more elements')
                    value = x
                    has_result = True
            if not has_result:
                raise TypeError('sequence contains no elements')
            return value

        def sum(self, selector=lambda x: x):
            return sum(map(selector, self._iterable()))
        def to_dict(self, key_selector, element_selector=lambda x: x):
            return {
                (key_selector(x), element_selector(x))
                for x in self._iterable()
            }
        def to_list(self):
            return list(self._iterable())
    return Enumerable(lambda: iterable)
</code></pre>

<p>So you'd be able to do things like this with any iterable sequence as you might do it in C#.</p>

<pre><code># save a linq query
query = linq(range(100))

# even numbers as strings
evenstrs = query.where(lambda i: i%2 == 0).select(str)

# build a different result using the same query instances
odds = query.where(lambda i: i%2 != 0)
smallnums = query.where(lambda i: i &lt; 50)
</code></pre>



<pre><code># dynamically build a query
query = linq(some_list_of_objects)

if some_condition:
    query = query.where(some_predicate)

if some_other_condition:
    query = query.where(some_other_predicate)

result = query.to_list()
</code></pre>

python linq iterator generator