如何使用类似LINQ的方法(select,where,orderby等)创建一个Python类,它是一个可迭代的包装器,而不使用扩展方法或猴子修补。 ?
这就是这个LinqCapable类能够在相关时返回它自己的类型(即流畅的设计)并支持延迟评估。
我只是在这里寻找一个片段作为起点。
答案 0 :(得分:1)
你应该返回一个“Linq能力类”来实现链接,而且你的实现并不是懒惰的:看看asq where method:它基于I过滤器,看起来是正确的...... 无论如何,根据我对你的问题和评论的理解,这是一个非常基本的实现
Location()
class LinqCapable(object):
def __init__(self, iterable=None):
self._iterable = iterable
self._predicates = []
def where(self, predicate):
chain = LinqCapable(self._iterable)
chain._predicates = self._predicates
chain._predicates.append(predicate)
return chain
def toArray(self):
for item in self._iterable:
isOk = True
for predicate in self._predicates:
if (not predicate(item)):
isOk = False
break
if (isOk):
yield item
我还添加了一个简单的select方法。 这里的目标是在可能的情况下聚合谓词和选择器,以避免低效的嵌套循环。
test = LinqCapable([1,2,3])
def pred1(l: int) -> bool:
return l>1
chain1 = test.where(pred1)
def pred2(l: int) -> bool:
return l<3
chain2 = chain1.where(pred2)
list(chain2.toArray())
所以一个例子是
class LinqCapable(object):
def __init__(self, iterable=None, predicates = [], selectors = [], tree=None):
self._iterable = iterable
self._predicates = list(predicates)
self._selectors = list(selectors)
self._tree = tree
def select(self, selector):
if (len(self._predicates) == 0):
chain = LinqCapable(self._iterable, [], self._selectors, self._tree)
chain._selectors.append(selector)
else:
chain = LinqCapable(None, [], [])
if (len(self._selectors) == 0):
chain._tree = LinqCapable(self._iterable, self._predicates, [], self._tree)
else:
chain._tree = self
chain._selectors.append(selector)
return chain
def where(self, predicate):
chain = LinqCapable(self._iterable, self._predicates, self._selectors, self._tree)
chain._predicates.append(predicate)
return chain
def enumerate(self):
if (self._tree != None):
self._iterable = list(self._tree.enumerate())
return self._cycle()
def _cycle(self):
for item in self._iterable:
for selector in self._selectors:
item = selector(item)
isOk = True
for predicate in self._predicates:
if (not predicate(item)):
isOk = False
break
if (isOk):
yield item
答案 1 :(得分:1)
仅为实现的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()