仅仅为实现的 linq 方法返回一个生成器是不够的,您需要让它返回一个包装器的实例,以便能够链接其他调用。
您可以创建一个可以重新包装 linq 实现的元类。所以有了这个,你可以只实现你想要支持的方法并使用一些特殊的装饰器来确保它保持可链接。
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 >= 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 < 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)
因此,您可以像在 C# 中那样使用任何可迭代的序列来执行此类操作。
# 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 < 50)
# 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()