Python:支持LINQ的类同时支持懒惰和流畅的设计 [英] Python: LINQ capable class supporting simultaneously laziness and fluent design
问题描述
如何使用类似LINQ的方法(select,where,orderby等)创建一个Python类,它是一个可迭代的包装器,而不使用扩展方法或猴子修补。
?
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. ?
这就是这个LinqCapable类在相关时能够返回自己的类型(即流畅的设计)并支持延迟评估。
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.
我只是在这里寻找一个片段作为起点。
I'm just looking here for a snippet as a starting point.
推荐答案
仅仅为实现的linq方法返回一个生成器是不够的,你需要让它返回一个包装器的实例才能链接其他调用。
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.
你可以创建一个可以重写linq实现的元类。因此,您可以实现您想要支持的方法并使用一些特殊的装饰器来确保它仍然是可链接的。
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.
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#中。
So you'd be able to do things like this with any iterable sequence as you might do it in 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()
这篇关于Python:支持LINQ的类同时支持懒惰和流畅的设计的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!