在python中执行Class.objects.filter(…)模式
我希望使用Model.objects.filter的django模型中使用的模式。。。跨数据构建筛选器。这可能是pandas的一个很好的用例,但我更感兴趣的是在尝试之前先改进我的python 如果我有以下数据:在python中执行Class.objects.filter(…)模式,python,python-3.x,class,inheritance,Python,Python 3.x,Class,Inheritance,我希望使用Model.objects.filter的django模型中使用的模式。。。跨数据构建筛选器。这可能是pandas的一个很好的用例,但我更感兴趣的是在尝试之前先改进我的python 如果我有以下数据: DATA = [ {'id': 1, 'name': 'brad', 'color':'red'}, {'id': 2, 'name': 'sylvia', 'color':'blue'}, ] 我希望构建类似于以下内容的内容: class MyData: ob
DATA = [
{'id': 1, 'name': 'brad', 'color':'red'},
{'id': 2, 'name': 'sylvia', 'color':'blue'},
]
我希望构建类似于以下内容的内容:
class MyData:
objects = <something>
并获得:
[
{'id': 2, 'name': 'sylvia', 'color':'blue'}
]
当然,我可以做一些简单的事情,比如:
res = [_ for _ in DATA if _['id'] > 1]
但我更感兴趣的是设计模式本身——示例的琐碎本质只是为了展示我希望实现的目标
正确地做到这一点的基本方法是什么?这里是django中的相关类:。如果您想要完整的django模型体验,即: 使用datapoint=MyDataname='johndoe',color='green',创建新的特征向量或数据条目。。。就像在django中一样:例如new_user=Userusername='johndoe',email='s'jd@jd.com'; 使用MyData.objects进行对象管理,如MyData.objects.filtercolor\uu eq='yellow'; 下面是一个关于逻辑的方法 首先,您需要一个简单的ObjectManager类: 导入集合 进口经营者 进口检验 类ObjectManagercollections.MutableSet: 定义初始自我: 这将保存自定义类中所有属性的列表,一次 启动 self.\u object\u attributes=无 self.\u theset=set def addself,项目: self.\u theset.additem def自身,项目: 自置项 定义自身: 将其返回给自己 def___________________________自已: 返回透镜组 定义包含自身,项目: 尝试: 返回自我设置中的项目。\u theset 除属性错误外: 返回错误 def set_attributeself,一个_对象: self.\u对象\u属性=[ inspect.getmembers中的[0]用于 对象lambda:notinspect.isroutinea 如果不是[0],则以“\uuuuuu”和[0]开头。以“\uuuuuuuu”结尾 ] def过滤器自身,**kwargs: 根据一个或多个条件过滤对象 如果存在多个过滤条件,则可以设置 组合模式为“和”或“或”。 mode=kwargs.pop'mode','or' ok_objects=set 对于千瓦(单位:千瓦): 如果以千瓦为单位的“\uuuuuuuuuuuu”: _千瓦,op=千瓦分体式 仅允许有效的运算符 在“lt”、“le”、“eq”、“ne”、“ge”、“gt”中断言op 其他: op=‘eq’ _千瓦=千瓦 _oper=getattroperator,op 仅允许访问有效的对象属性 在self.\u object\u属性中断言 n_对象= 在self中为obj创建obj 如果操作属性为,则为千瓦,千瓦[kw] 如果模式=='和': 如果n_对象: 确定对象=确定对象。相交n对象\ 如果确定对象,则设置其他对象 其他: 返回集 其他: ok_objects.updaten_objects 返回ok_对象 您可以随意添加“获取”或“创建”、“创建”等。 现在,将该类的一个实例作为属性附加到MyData类,并确保将所有新对象添加到该类: 类别MyData: 启动对象管理器 对象=对象管理器 定义初始自身、uid、名称、颜色: self.uid=uid self.name=名称 self.color=颜色 在创建时填充可查询属性的列表 一审 如果不是lenself.objects: self.objects.set_attributeself 向对象管理器添加任何新实例 self.objects.addself 现在可以导入特征向量: 数据=[ {'uid':1,'name':'brad','color':'red'}, {'uid':2,'name':'sylvia','color':'blue'}, ] 对于数据中的dat: 我的数据**dat 或创建新实例: d1=MyDatauid=10,名称='john',颜色='黄色' 并使用管理器过滤对象: 打印[MyData.objects.filteruid\uu ge=10中md的md.name] >[约翰] 打印[MyData.objects.filtermode='and',uid\uu ge=1,name\uu eq='john'中md的md.name] >[约翰] 打印[MyData.objects.filtermode='or',uid\uu le=4,name\uu eq='john'中md的md.name] >[“约翰”、“布拉德”、“西尔维亚”] 如果您不能或不想更改您想要对象管理器的类,并且您愿意四处修补,请注意,我不是在宣传这个!您甚至可以创建一个ObjectManager,该ObjectManager可以连接到任意类,但在定义或启动某些实例之后,内置类型将无法工作 其思想是对目标类进行monkey patch _init _;操作,并在ObjectManager实例的初始化时添加objects属性: 导入gc 进口检验 导入集合 进口经营者 进口包装 非标准库>pip安装包 类ObjectManagercollections.MutableSet: 定义初始自我,将其连接到: self.\u object\u attributes=无 将self添加为类属性 将_附加到对象=自身 目标类的猴子补丁初始化 @wrapt.patch_函数_wrapperattach_到,'._初始化_' 定义n_initwrapped,实例,参数,kwargs: 已包装*args,**kwargs c_objects=实例。\u类\u.objects 如果不是c_对象: c_objects.set_attributes实例 c_objects.addinstance 确保对现有实例进行更新 self.\u theset=setobj用于gc中的obj。获取\u对象如果是instanceobj,请将\u附加到 如果实例存在,则已获取属性 如果是自组: self.set\u attributes nextiterself.\u theset ... 其余部分与上述版本相同 现在,您将如何使用它: 类别MyData: 定义初始自身、uid、名称、颜色: self.uid=uid self.name=名称 self.color=颜色 创建一些实例 数据=[ {'uid':1,'name':'brad','color':'red'}, {'uid':2,'name':'sylvia','color':'blue'}, ] 我的_数据=[] 对于数据中的dat: my_data.appendmyData**dat追加它们只是为了有一个引用 假设现在您决定使用对象管理器 简单地做: ObjectManagerMyData 你做到了: 打印[MyData.objects.filtermode='or',uid\uu le=4,name\uu eq='john'中md的md.name] >[“布拉德”,“西尔维亚”] 此外,还包括从现在开始创建的任何对象: d1=MyDatauid=10,名称='john',颜色='黄色' 打印[MyData.objects.filtermode='or',uid\uu le=4,name\uu eq='john'中md的md.name] >[“布拉德”、“西尔维亚”、“约翰”] 这就是你的意思吗 此解决方案不依赖于外部库,也不使用 **Kwarg、生成器/闭包和@property装饰器。因此,从学习的角度来看,这可能很有趣 如果您设法使用Django读取列表中的数据,那么就Django兼容性而言,这可能会比我的代码好得多。 这完全取决于你的目标是什么。完美模仿django过滤器或学习如何做一个不那么完美的模仿,但有完整的源代码没有依赖性
DATA = [
{'id': 1, 'name': 'brad', 'color':'red'},
{'id': 2, 'name': 'sylvia', 'color':'blue'},
{'id': 3, 'name': 'paul', 'color':'red'},
{'id': 4, 'name': 'brandon', 'color':'yello'},
{'id': 5, 'name': 'martin', 'color':'green'},
{'id': 6, 'name': 'annie', 'color':'gray'},
]
class UnknownOperator(Exception):
""" custom exception """
class FilterData:
def __init__(self, data):
self.data = data
def _filter_step(self, key, value, data):
if not "__" in key:
return (entry for entry in data if entry[key] == value)
else:
key, operator = key.split("__")
if operator == "gt": # greater than
return (entry for entry in data if entry[key] > value)
elif operator == "lt": # less than
return (entry for entry in data if entry[key] < value)
elif operator == "startswith": # starts with
return (entry for entry in data if entry[key].startswith(value))
elif operator == "in": # starts with
return (entry for entry in data if entry[key] in value)
else:
raise UnknownOperator("operator %s is unknown" % operator)
def _exclude_step(self, key, value, data):
if not "__" in key:
return (entry for entry in data if entry[key] != value)
else:
key, operator = key.split("__")
if operator == "gt": # greater than
return (entry for entry in data if entry[key] <= value)
elif operator == "lt": # less than
return (entry for entry in data if entry[key] >= value)
elif operator == "startswith": # starts with
return (entry for entry in data if not entry[key].startswith(value))
elif operator == "in": # starts with
return (entry for entry in data if entry[key] not in value)
else:
raise UnknownOperator("operator %s is unknown" % operator)
def filter(self, **kwargs):
data = (entry for entry in self.data)
for key, value in kwargs.items():
data = self._filter_step(key, value, data)
return FilterData(data)
def exclude(self, **kwargs):
data = (entry for entry in self.data)
for key, value in kwargs.items():
data = self._exclude_step(key, value, data)
return FilterData(data)
def all(self):
return FilterData(self.data)
def count(self):
cnt = 0
for cnt, entry in enumerate(self.data, 1):
pass
return cnt
def __iter__(self):
for entry in self.data:
yield entry
# make it even more look like django managers / filters
class DataManager:
def __init__(self, data):
self.data = data
@property
def objects(self):
return FilterData(self.data)
fdata = FilterData(DATA)
assert [v["id"] for v in fdata.filter(name="paul")] == [3]
assert [v["id"] for v in fdata.filter(color="red")] == [1, 3]
assert [v["id"] for v in fdata.filter(id__gt=2)] == [3, 4, 5, 6]
assert [v["id"] for v in fdata.filter(color__startswith="gr")] == [5, 6]
fmgr = DataManager(DATA)
assert [v["id"] for v in fmgr.objects.filter(name="paul")] == [3]
assert [v["id"] for v in fmgr.objects.filter(color="red")] == [1, 3]
assert [v["id"] for v in fmgr.objects.filter(id__gt=2)] == [3, 4, 5, 6]
assert [v["id"] for v in fmgr.objects.filter(color__startswith="gr")] == [5, 6]
assert [v["id"] for v in fmgr.objects.filter(color__startswith="gr", id__lt=6)] == [5]
assert [v["id"] for v in fmgr.objects.filter(color__startswith="gr", id__lt=6)] == [5]
assert [v["id"] for v in fmgr.objects.filter(color__startswith="gr").filter(id__lt=6)] == [5]
assert fmgr.objects.filter(color__startswith="gr").filter(id__lt=6).count() == 1
assert fmgr.objects.filter(id__gt=2).count() == 4
assert fmgr.objects.count() == 6
assert [v["id"] for v in fmgr.objects.all()] == list(range(1, 7))
下面是一个示例,其中我创建了一个新的NoteQuerySet类 继承自django.db.models.QuerySet。之后,我利用了 作为_manager方法,通过这样做,对象管理器被覆盖 经理应该拥有的所有操作 因此,为了得到您想要的结果,我创建了一个新的自定义过滤器 方法,该方法对NoteQuerySet.data进行操作,并使用字典 跟踪并轻松添加新过滤器 如您所见,我正在创建一个新的自定义过滤器,而不是覆盖 对象。过滤器;这是有意的,因此不会丢失本机筛选。 还要注意操作符内置模块,它可以轻松地将字符串映射到 行动 models.py results.html 更新:非django实施: __in操作接受一个值列表。这段代码假设您希望所有这些都出现在标记中,这就是为什么我们使用allitem.opentry[item.key],v代表item.value中的v。OP希望这样做MyData.objects.filterid>1 让我们面对现实吧 问题是Python贪婪地急切地计算表达式,而不像Haskell那样懒惰。 注意让人心神不宁的事情 Python在调用筛选器之前计算id>1。如果现在可以停止计算,那么可以将未计算的表达式传递给filter函数 但是,如果将表达式包含在函数中,则可以将表达式计算延迟到需要时。这就是我的想法 如果我们能够实现,函数接口将是filterlambda:id>1。 这个接口将是超级通用的,因为任何Python表达式都可以被传递和滥用 实施情况 如果调用lambda或表达式id>1的任何其他函数,Python将根据调用函数的上下文在本地、封闭、全局范围或内置中查找名称id 如果我们可以在Python在内置函数中找到id之前在查找路径的某个地方引入一个名为id的对象,那么我们可以重新定义表达式的语义 我要用eval来计算给定上下文中的表达式
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)
我将dict.copy传递给eval,因为eval修改它的globals对象
在模型类的上下文中查看它的实际操作
即使不要求推荐,你的问题也太宽泛了。最好从尝试构建这样一个类开始,如果您的尝试有问题,请询问。@sanyash更新了问题。您的问题仍然存在一些问题。objects=是伪代码,最好用真实代码替换它。ModelManager-是什么?id>1-将引发错误
id内置函数无法与int进行比较。该问题缺乏对类的清晰设计。正如我所说的,最好自己尝试创建这个类。之后,您可以问更清楚的问题。您将无法执行类似于:MyData.objects.filterid>1的操作,但您可以使用**kwargs模拟类似django过滤器的操作。和解析键。有趣的是,您接受的唯一答案不遵循,而是使用未命名函数。但是你明确地把你的问题和django联系起来。。。只是说。修复了初始版本。第一个版本没有为生成器创建正确的闭包。因此引入了一个“过滤器”步骤,使其具有一个不可更改的键,即val CLOSURE(关闭)。但是,您实际上如何处理FilterData.objects.filtercolor=red的模式呢?不仅仅是FilterDatadata.filtercolor=read。即-如何在通话中添加另一个“步骤”或分段?为此,如果有帮助,您可以假设数据已经加载/存储。确定将发布更改的版本。对于那些想让django看起来很像的人,可以将对象与一个类一起使用。其他人只想使用筛选函数,但不想一直键入对象初始解决方案。更改后的版本看起来不错。我已经对你的答案投了赞成票,但我会等一段时间,以防其他人回答,因为我刚刚提出了悬赏。但有一个问题:你有fmg.objects.filter…-如何使其仅为fmg.objects.filter,即不调用对象的方法?作为参考,这里是django的Queryset代码:。此解决方案可能比我的更完整。但是,它需要django作为依赖项。但这不是一个真正的问题好答案@slackmart,顺便问一下,有什么解决方案可以得到递归dict吗?例如:{id:1,name:brad,parent:{id:2,name:john}},所以预期的ORM将是d.objects.filterparent\uuuuuu name\uuuuuuuu eq=johnalso在列表中搜索怎么样?e、 g:d.objects.filtertags\uu in=[python,django],mwane当标记是一个列表时,例如:tags:[javascript,python]@binpy,检查更新的答案。我在非django实现中添加了uu。另一个要求也可以实现,但这需要更多的时间。这个猴子补丁看起来很麻烦。因此,基本上,即使您无法编辑类定义,您也可以将此“ObjectManager”挂接到任何类?@NKahle来自内置类型(如list、tuple等)的一部分。是的。
import operator
from collections import namedtuple
from django.db import models
class NoteQuerySet(models.QuerySet):
data = [
{'id': 1, 'name': 'brad', 'color':'red'},
{'id': 2, 'name': 'sylvia', 'color':'blue'},
{'id': 3, 'name': 'sylwia', 'color':'green'},
{'id': 4, 'name': 'shane', 'color':'red'},
]
allowed_operations = {'gt': operator.gt, 'lt': operator.lt, 'eq': operator.eq}
def custom_filter(self, **kwargs):
"""
>>> kwargs = {'name': 'sylwia', 'id__gt': 1}
dict_items([('name', 'sylwia'), ('id__gt', 1)])
"""
operation = namedtuple('Q', 'op key value')
def parse_filter(item):
"""item is expected to be a tuple with exactly two elements
>>> parse_filter(('id__gt', 2))
Q(op=<built-in function gt>, key='id', value=2)
"""
key, *op = item[0].split('__')
# no value after __ means exact value query, e.g. name='sylvia'
op = op or ['eq']
return operation(self.allowed_operations[op[0]], key, item[1])
filtered_data = self.data.copy()
for item in map(parse_filter, kwargs.items()):
filtered_data = [
entry for entry in filtered_data if item.op(entry[item.key], item.value)
]
return filtered_data
class Note(models.Model):
text = models.CharField(max_length=250)
objects = NoteQuerySet.as_manager()
from django.views.generic import ListView
from .models import Note
class ResultsApplicationView(ListView):
model = Note
template_name = 'results.html'
def get_context_data(self, **kwargs):
kwargs = super().get_context_data(**kwargs)
if 'extra' not in kwargs:
kwargs['extra'] = self.model.objects.custom_filter(id__lt=3, color='red')
return kwargs
<h1>Notes</h1>
{% for note in object_list %}
{{note}}
{% endfor %}
{{ extra }}
import operator
from collections import namedtuple
class DataQuerySet:
allowed_operations = {
'gt': operator.gt,
'lt': operator.lt,
'eq': operator.eq,
'in': operator.contains,
}
def __init__(self, data):
self.data = data
def filter(self, **kwargs):
"""
>>> kwargs = {'name': 'sylwia', 'id__gt': 1}
>>> DataQuerySet().filter(**kwargs)
[{'id': 3, 'name': 'sylwia', 'color': 'green'}]
"""
operation = namedtuple('Q', 'op key value')
def parse_filter(item):
"""item is expected to be a tuple with exactly two elements
>>> parse_filter(('id__gt', 2))
Q(op=<built-in function gt>, key='id', value=2)
>>> parse_filter(('id__ ', 2))
Q(op=<built-in function eq>, key='id', value=2)
>>> parse_filter(('color__bad', 'red'))
Traceback (most recent call last):
...
AssertionError: 'bad' operation is not allowed
"""
key, *op = item[0].split('__')
# no value after __ means exact value query, e.g. name='sylvia'
op = ''.join(op).strip() or 'eq'
assert op in self.allowed_operations, f'{repr(op)} operation is not allowed'
return operation(self.allowed_operations[op], key, item[1])
filtered_data = self.data.copy()
results = []
for item in map(parse_filter, kwargs.items()):
for entry in filtered_data:
if item.op == operator.contains and all(item.op(entry[item.key], v) for v in item.value):
results.append(entry)
elif item.op(entry[item.key], item.value):
results.append(entry)
return results
class Data:
def __init__(self, data):
self._data = DataQuerySet(data)
@property
def objects(self):
return self._data
if __name__ == '__main__':
data = [
{'id': 1, 'name': 'brad', 'color': 'red', 'tags': ['c++', 'javascript']},
{'id': 2, 'name': 'sylvia', 'color': 'blue', 'tags': ['c++']},
{'id': 3, 'name': 'sylwia', 'color': 'green', 'tags': ['c++', 'javascript', 'python']},
{'id': 4, 'name': 'shane', 'color': 'red', 'tags': ['c++', 'javascript', 'python']},
]
d = Data(data)
print('Entries with id greater than 2:', d.objects.filter(id__gt=2))
print('Entries with color="green":', d.objects.filter(color='green'))
print('Entries with "python" in tags:', d.objects.filter(tags__in=['python']))
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)
In [1]: class Data(Model):
...: name = str()
...: id = int()
...: color = str()
...:
In [2]: Data.objects.create(**{"id": 1, "name": "brad", "color": "red"})
In [3]: Data.objects.create(**{"id": 2, "name": "sylvia", "color": "blue"})
In [4]: Data.objects.create(**{"id": 3, "name": "paul", "color": "red"})
In [5]: Data.objects.create(**{"id": 4, "name": "brandon", "color": "yello"})
In [6]: Data.objects.create(**{"id": 5, "name": "martin", "color": "green"})
In [7]: Data.objects.create(**{"id": 6, "name": "annie", "color": "gray"})
In [8]: pprint([vars(obj) for obj in Data.objects.filter(lambda: id == 1)])
[{'color': 'red', 'id': 1, 'name': 'brad'}]
In [9]: pprint([vars(obj) for obj in Data.objects.filter(lambda: 1 <= id <= 2)])
[{'color': 'red', 'id': 1, 'name': 'brad'},
{'color': 'blue', 'id': 2, 'name': 'sylvia'}]
In [10]: pprint([vars(obj) for obj in Data.objects.filter(lambda: color == "blue")])
[{'color': 'blue', 'id': 2, 'name': 'sylvia'}]
In [11]: pprint([vars(obj) for obj in Data.objects.filter(lambda: "e" in color and (name is "brad" or name is "sylvia"))])
[{'color': 'red', 'id': 1, 'name': 'brad'},
{'color': 'blue', 'id': 2, 'name': 'sylvia'}]
In [12]: pprint([vars(obj) for obj in Data.objects.filter(lambda: id % 2 == 1)])
[{'color': 'red', 'id': 1, 'name': 'brad'},
{'color': 'red', 'id': 3, 'name': 'paul'},
{'color': 'green', 'id': 5, 'name': 'martin'}]
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)])