我正在尝试重载一个类的in
运算符以返回一个非bool对象,但它似乎无论如何都要强制转换。这是我的用例:
class Dataset(object):
def __init__(self):
self._filters = []
def filter(self, f):
self._filters.append(f)
return self
class EqualFilter(object):
def __init__(self, field, val):
...
class SubsetFilter(object):
def __init__(self, field, vals):
...
class FilterBuilder(object):
def __init__(self, field):
self._field = field
def __eq__(self, val):
return EqualFilter(self._field, val)
def __contains__(self, vals):
return SubsetFilter(self._field, vals)
veggie = FilterBuilder('veggie')
fruit = FilterBuilder('fruit')
ds = Dataset().filter(veggie == 'carrot').filter(fruit in ['apple', 'orange'])
在代码的最后,ds
包含EqualFilter
的{{1}}和veggie == 'carrot'
的{{1}}。有没有办法让ds最终得到True
?
答案 0 :(得分:4)
这里真正的问题是,正如the documentation所说:
对于定义
__contains__()
方法的用户定义类,x in y
当且仅当y.__contains__(x)
为真时才为真。
user2357112的答案比我更好地解释了这一点。您正在呼叫list.__contains__
,而不是FilterBuilder.__contains__
。
但为什么会这样呢?
那么,它还有什么用呢?
想象一下,3 in [1, 2, 3]
调用int.__contains__
。 3 in {1, 2, 3}
和3 in my_custom_sorted_bintree
也是如此。怎么可能实现int.__contains__(container)
?当然不是通过迭代容器。这将意味着缓慢,详尽的搜索在集合和bintrees中查找事物,这将打败整个点。我的bintree类可能甚至不可迭代,但仍然可能有成员资格的概念。
但是如果他们拨打list.__contains__
,set.__contains__
和CustomSortedBintree.__contains__
怎么办?难道他们不必知道int,str和其他可能给你的东西吗?一点都不。列表只需要知道如何比较arg == elem
的每个元素。一组还需要知道如何调用hash(arg)
。 bintree还必须知道如何调用arg < elem
。但是你不需要知道arg
的类型。
您可能想知道如何处理这个问题。有两种常见的解决方案。
1:您可以轻松创建FilterList
课程。然后,你只需写:
fruit in FilterList('apple', 'orange')
1.5:或者,通过更多的工作,你可以建立一个更通用的“价值持有者”:
fruit in const(['apple', 'orange'])
2:或者,您可以编写FilterBuilder.in_
方法。然后你写:
fruit.in_(['apple', 'orange'])
...或者,如果您愿意:
fruit.in_('apple', 'orange')
我见过的大多数库都提供了第二个(sqlalchemy),或提供了两个,但在他们的教程(appscript)中使用了第二个,尽管“quick-lambda”库通常与第一个库的通用版本一起使用。
但是你应该考虑自己的用例权衡。通常,第一个更容易实现,更明确,并且具有子过滤器/子查询可以返回充当FilterList
的东西的优点;第二个是不那么冗长,可以说更容易阅读。
如果两者都不可接受,您可以考虑为类似Python的DSL编写解析器,而不是尝试通过表达式模板从实际的Python代码构建DSL。或者使用类似MacroPy
的东西(我认为甚至有一个类似于你正在寻找的例子)以及不需要“const”和朋友的快速lambda宏。
答案 1 :(得分:3)
这里有两个问题。首先,in
始终将__contains__
的结果投射到bool
,因此您无法找到所需内容。第二个问题是
fruit in ['apple', 'orange']
呼叫
['apple', 'orange'].__contains__(fruit)
in
的左操作数无法覆盖操作符,因此这也会打败你想要做的事情。
答案 2 :(得分:1)
没有。 list.__contains__
总是返回bool
,并且C类型不能被monkeypatched(你也不应该考虑这样做,因为你可能会破坏其他代码)。