Python:支持LINQ的类同时支持懒惰和流畅的设计 [英] Python: LINQ capable class supporting simultaneously laziness and fluent design

查看:169
本文介绍了Python:支持LINQ的类同时支持懒惰和流畅的设计的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何使用类似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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆