重载Python'in'以返回非bool

时间:2013-09-12 00:30:46

标签: python operator-overloading

我正在尝试重载一个类的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

3 个答案:

答案 0 :(得分:4)

这里真正的问题是,正如the documentation所说:

  

对于定义__contains__()方法的用户定义类,x in y当且仅当y.__contains__(x)为真时才为真。

另见herehere

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(你也不应该考虑这样做,因为你可能会破坏其他代码)。