<p>OP想要这样做<code>MyData.objects.filter(id>1)</code>。在</p>
<p>让我们面对现实吧。在</p>
<p>问题是Python贪婪(急于计算表达式),而不像Haskell那样懒惰。<br/>
注意<a href="https://www.youtube.com/watch?v=pkCLMl0e_0k" rel="nofollow noreferrer">David Beazley - Lambda Calculus from the Ground Up - PyCon 2019</a>有没有让人心烦意乱的东西。在</p>
<p>Python在调用<code>filter</code>之前计算<code>id > 1</code>。如果现在可以停止求值,可以将未求值的表达式传递给<code>filter</code>函数。在</p>
<p>但是,如果我们将表达式括在函数中,则可以将表达式求值延迟到需要时。这就是我的想法。在</p>
<p>如果我们可以实现函数接口,它将是<code>filter(lambda: id > 1)</code>。
这个接口将是超级通用的,因为任何Python表达式都可以被传递和滥用。在</p>
<p>实施情况</p>
<p>{{cdda}如果调用的是另一个函数{cdda},则调用该函数。在</p>
<p>如果我们可以在Python在<code>builtins</code>中找到<code>id</code>之前,在查找路径的某个地方引入一个名为<code>id</code>的对象,我们就可以重新定义表达式的语义。在</p>
<p>我将用<code>eval</code>来实现它,它计算给定上下文中的表达式。在</p>
<pre><code>DATA = [
{'id': 1, 'name': 'brad', 'color':'red'},
{'id': 2, 'name': 'sylvia', 'color':'blue'},
]
def myfilter(a_lambda):
return filter(lambda obj: eval(a_lambda.__code__, obj.copy()),
DATA)
</code></pre>
<p>我将一个<code>dict.copy</code>传递给<code>eval</code>,因为<code>eval</code>修改了它的<code>globals</code>对象。在</p>
<p>在<code>Model</code>类的上下文中查看它的实际操作</p>
^{pr2}$
<p><code>Data</code>类继承自<code>Model</code>。{{{cda><cda>{cda}给了一个名为^ cda}的类{cda},它是一个名为^ cda}的类。在</p>
<p>当从子类访问<code>objects</code>属性时,<code>MetaManager</code>将一个<code>Manager</code>实例返回给<code>Model</code>的子类。<code>MetaManger</code>标识访问类并将其传递给<code>Manager</code>实例。
<code>Manager</code>处理对象的创建、持久化和获取。在</p>
<p>为了简单起见,db被实现为<code>Manager</code>的类属性。在</p>
<p>为了停止通过函数滥用全局对象,<code>filter</code>函数在未传递lambda时引发异常。在</p>
<pre><code>from collections import defaultdict
from collections.abc import Callable
class MetaManager:
def __get__(self, obj, objtype):
if obj is None:
return Manager(objtype)
else:
raise AttributeError(
"Manger isn't accessible via {} instances".format(objtype)
)
class Manager:
_store = defaultdict(list)
def __init__(self, client):
self._client = client
self._client_name = "{}.{}".format(client.__module__, client.__qualname__)
def create(self, **kwargs):
self._store[self._client_name].append(self._client(**kwargs))
def all(self):
return (obj for obj in self._store[self._client_name])
def filter(self, a_lambda):
if a_lambda.__code__.co_name != "<lambda>":
raise ValueError("a lambda required")
return (
obj
for obj in self._store[self._client_name]
if eval(a_lambda.__code__, vars(obj).copy())
)
class Model:
objects = MetaManager()
def __init__(self, **kwargs):
if type(self) is Model:
raise NotImplementedError
class_attrs = self.__get_class_attributes(type(self))
self.__init_instance(class_attrs, kwargs)
def __get_class_attributes(self, cls):
attrs = vars(cls)
if "objects" in attrs:
raise AttributeError(
'class {} has an attribute named "objects" of type "{}"'.format(
type(self), type(attrs["objects"])
)
)
attrs = {
attr: obj
for attr, obj in vars(cls).items()
if not attr.startswith("_") and not isinstance(obj, Callable)
}
return attrs
def __init_instance(self, attrs, kwargs_dict):
for key, item in kwargs_dict.items():
if key not in attrs:
raise TypeError('Got an unexpected key word argument "{}"'.format(key))
if isinstance(item, type(attrs[key])):
setattr(self, key, item)
else:
raise TypeError(
"Expected type {}, got {}".format(type(attrs[key]), type(item))
)
if __name__ == "__main__":
from pprint import pprint
class Data(Model):
name = str()
id = int()
color = str()
Data.objects.create(**{"id": 1, "name": "brad", "color": "red"})
Data.objects.create(**{"id": 2, "name": "sylvia", "color": "blue"})
Data.objects.create(**{"id": 3, "name": "paul", "color": "red"})
Data.objects.create(**{"id": 4, "name": "brandon", "color": "yello"})
Data.objects.create(**{"id": 5, "name": "martin", "color": "green"})
Data.objects.create(**{"id": 6, "name": "annie", "color": "gray"})
pprint([vars(obj) for obj in Data.objects.filter(lambda: id == 1)])
pprint([vars(obj) for obj in Data.objects.filter(lambda: 1 <= id <= 2)])
pprint([vars(obj) for obj in Data.objects.filter(lambda: color == "blue")])
pprint(
[
vars(obj)
for obj in Data.objects.filter(
lambda: "e" in color and (name is "brad" or name is "sylvia")
)
]
)
pprint([vars(obj) for obj in Data.objects.filter(lambda: id % 2 == 1)])
</code></pre>