4

我试图重载in一个类的运算符以返回一个非布尔对象,但它似乎无论如何都会强制转换。这是我的用例:

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包含一个EqualFilterforveggie == 'carrot'Truefor fruit in ['apple', 'orange']。有没有办法让 ds 以 a 结束SubsetFilter

4

3 回答 3

4

这里有两个问题。首先,in总是将结果转换__contains__为 a bool,所以你正在寻找的东西是不可能的。第二个问题是

fruit in ['apple', 'orange']

来电

['apple', 'orange'].__contains__(fruit)

左操作数in无法覆盖运算符,因此这也会破坏您尝试做的事情。

于 2013-09-12T00:45:19.333 回答
4

这里真正的问题是,正如文档所说:

对于定义__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 宏)。

于 2013-09-12T01:48:35.180 回答
1

不,list.__contains__ 总是返回 a bool,并且 C 类型不能被猴子补丁(你也不应该考虑这样做,因为你可能会破坏其他代码)。

于 2013-09-12T00:38:47.863 回答