在 python 中执行 Class.objects.filter(...) 模式

Dav*_*542 5 python inheritance class python-3.x

我希望使用 django 模型中使用的模式来Model.objects.filter(...)构建跨数据的过滤器。这可能是 pandas 的一个很好的用例,但我更感兴趣的是在尝试之前改进我的 python(首先)。

如果我有以下数据:

DATA = [
    {'id': 1, 'name': 'brad', 'color':'red'},
    {'id': 2, 'name': 'sylvia', 'color':'blue'},
]
Run Code Online (Sandbox Code Playgroud)

我想构建类似于以下内容的东西:

class MyData:
    objects = <something>
Run Code Online (Sandbox Code Playgroud)

并将objects等效项设置为“ModelManager”,然后从那里进行过滤,以便我可以调用:

MyData.objects.filter(id>1)
Run Code Online (Sandbox Code Playgroud)

并得到:

[
    {'id': 2, 'name': 'sylvia', 'color':'blue'}
]
Run Code Online (Sandbox Code Playgroud)

当然,我可以做一些简单的事情:

res = [_ for _ in DATA if _['id'] > 1]
Run Code Online (Sandbox Code Playgroud)

但我对设计模式本身更感兴趣——这个例子的琐碎本质只是为了展示我想要完成的任务。

正确执行此操作的良好基本方法是什么?这是 django 中的相关类:https://github.com/django/django/blob/master/django/db/models/query.py#L185

Niz*_*med 4

OP 想要这样做MyData.objects.filter(id>1)

\n\n

面对现实吧。

\n\n

问题是 Python 是贪婪的(急切地计算表达式),而不是像 Haskell 那样懒惰。
\n观看David Beazley - 从头开始​​的 Lambda 演算 - PyCon 2019,了解令人费解的 \xce\xbb 事情。

\n\n

Pythonid > 1在调用之前进行计算filter。如果我们现在可以停止计算,我们可以将未计算的表达式传递给函数filter

\n\n

但是,如果我们将表达式包含在函数中,则可以将表达式求值延迟到需要时为止。这就是想法。

\n\n

如果我们能够实现它,函数接口就会是filter(lambda: id > 1)。\n这个接口将非常通用,因为任何 Python 表达式都可以传递和滥用。

\n\n

实施;

\n\n

如果我们使用表达式调用 lambda 或任何其他函数,Python 会在本地、封闭、全局作用域中id > 1查找名称,或者根据调用函数的上下文查找名称。idbuiltins

\n\n

如果我们可以在idPython 查找之前在查找路径中的某个位置引入一个具有该名称的对象,我们就可以重新定义表达式的语义。 idbuiltins

\n\n

我将用它来eval计算给定上下文中的表达式。

\n\n
DATA = [\n    {\'id\': 1, \'name\': \'brad\', \'color\':\'red\'},\n    {\'id\': 2, \'name\': \'sylvia\', \'color\':\'blue\'},\n]\n\ndef myfilter(a_lambda):\n    return filter(lambda obj: eval(a_lambda.__code__, obj.copy()),\n    DATA)\n
Run Code Online (Sandbox Code Playgroud)\n\n

我将 a 传递dict.copyeval因为eval修改了它的globals对象。

\n\n

Model在课堂上查看它的实际应用

\n\n
In [1]: class Data(Model):\n   ...:     name = str()\n   ...:     id = int()\n   ...:     color = str()\n   ...: \n\nIn [2]: Data.objects.create(**{"id": 1, "name": "brad", "color": "red"})\n\nIn [3]:     Data.objects.create(**{"id": 2, "name": "sylvia", "color": "blue"})\n\nIn [4]:     Data.objects.create(**{"id": 3, "name": "paul", "color": "red"})\n\nIn [5]:     Data.objects.create(**{"id": 4, "name": "brandon", "color": "yello"})\n\nIn [6]:     Data.objects.create(**{"id": 5, "name": "martin", "color": "green"})\n\nIn [7]:     Data.objects.create(**{"id": 6, "name": "annie", "color": "gray"})\n\nIn [8]: pprint([vars(obj) for obj in Data.objects.filter(lambda: id == 1)])\n[{\'color\': \'red\', \'id\': 1, \'name\': \'brad\'}]\n\nIn [9]: pprint([vars(obj) for obj in Data.objects.filter(lambda: 1 <= id <= 2)])\n[{\'color\': \'red\', \'id\': 1, \'name\': \'brad\'},\n {\'color\': \'blue\', \'id\': 2, \'name\': \'sylvia\'}]\n\nIn [10]: pprint([vars(obj) for obj in Data.objects.filter(lambda: color == "blue")])\n[{\'color\': \'blue\', \'id\': 2, \'name\': \'sylvia\'}]\n\nIn [11]: pprint([vars(obj) for obj in Data.objects.filter(lambda: "e" in color and (name is "brad" or name is "sylvia"))])\n[{\'color\': \'red\', \'id\': 1, \'name\': \'brad\'},\n {\'color\': \'blue\', \'id\': 2, \'name\': \'sylvia\'}]\n\nIn [12]: pprint([vars(obj) for obj in Data.objects.filter(lambda: id % 2 == 1)])\n[{\'color\': \'red\', \'id\': 1, \'name\': \'brad\'},\n {\'color\': \'red\', \'id\': 3, \'name\': \'paul\'},\n {\'color\': \'green\', \'id\': 5, \'name\': \'martin\'}]\n
Run Code Online (Sandbox Code Playgroud)\n\n

该类Data继承自Model. 给出ModelData方法__init__和一个名为的类属性objects,该属性指向MetaManager一个描述符实例。

\n\n

在从子类访问属性时,将实例MetaManager返回Manager给子类。标识访问类并将其传递给实例。\n处理对象创建、持久化和获取。ModelobjectsMetaMangerManagerManager

\n\n

为了简单起见,db 被实现为 的类属性Manager

\n\n

为了阻止通过函数滥用全局对象,filter如果未传递 lambda,函数会引发异常。

\n\n
from collections import defaultdict\nfrom collections.abc import Callable\n\n\nclass MetaManager:\n    def __get__(self, obj, objtype):\n        if obj is None:\n            return Manager(objtype)\n        else:\n            raise AttributeError(\n                "Manger isn\'t accessible via {} instances".format(objtype)\n            )\n\n\nclass Manager:\n    _store = defaultdict(list)\n\n    def __init__(self, client):\n        self._client = client\n        self._client_name = "{}.{}".format(client.__module__, client.__qualname__)\n\n    def create(self, **kwargs):\n        self._store[self._client_name].append(self._client(**kwargs))\n\n    def all(self):\n        return (obj for obj in self._store[self._client_name])\n\n    def filter(self, a_lambda):\n        if a_lambda.__code__.co_name != "<lambda>":\n            raise ValueError("a lambda required")\n\n        return (\n            obj\n            for obj in self._store[self._client_name]\n\n            if eval(a_lambda.__code__, vars(obj).copy())\n        )\n\n\nclass Model:\n    objects = MetaManager()\n\n    def __init__(self, **kwargs):\n        if type(self) is Model:\n            raise NotImplementedError\n\n        class_attrs = self.__get_class_attributes(type(self))\n\n        self.__init_instance(class_attrs, kwargs)\n\n    def __get_class_attributes(self, cls):\n        attrs = vars(cls)\n        if "objects" in attrs:\n            raise AttributeError(\n                \'class {} has an attribute named "objects" of type "{}"\'.format(\n                    type(self), type(attrs["objects"])\n                )\n            )\n        attrs = {\n            attr: obj\n            for attr, obj in vars(cls).items()\n            if not attr.startswith("_") and not isinstance(obj, Callable)\n        }\n        return attrs\n\n    def __init_instance(self, attrs, kwargs_dict):\n        for key, item in kwargs_dict.items():\n            if key not in attrs:\n                raise TypeError(\'Got an unexpected key word argument "{}"\'.format(key))\n            if isinstance(item, type(attrs[key])):\n                setattr(self, key, item)\n            else:\n                raise TypeError(\n                    "Expected type {}, got {}".format(type(attrs[key]), type(item))\n                )\n\n\nif __name__ == "__main__":\n    from pprint import pprint\n\n    class Data(Model):\n        name = str()\n        id = int()\n        color = str()\n\n    Data.objects.create(**{"id": 1, "name": "brad", "color": "red"})\n    Data.objects.create(**{"id": 2, "name": "sylvia", "color": "blue"})\n    Data.objects.create(**{"id": 3, "name": "paul", "color": "red"})\n    Data.objects.create(**{"id": 4, "name": "brandon", "color": "yello"})\n    Data.objects.create(**{"id": 5, "name": "martin", "color": "green"})\n    Data.objects.create(**{"id": 6, "name": "annie", "color": "gray"})\n\n    pprint([vars(obj) for obj in Data.objects.filter(lambda: id == 1)])\n    pprint([vars(obj) for obj in Data.objects.filter(lambda: 1 <= id <= 2)])\n    pprint([vars(obj) for obj in Data.objects.filter(lambda: color == "blue")])\n    pprint(\n        [\n            vars(obj)\n            for obj in Data.objects.filter(\n                lambda: "e" in color and (name is "brad" or name is "sylvia")\n            )\n        ]\n    )\n    pprint([vars(obj) for obj in Data.objects.filter(lambda: id % 2 == 1)])\n
Run Code Online (Sandbox Code Playgroud)\n