【问题标题】:Error implementing __contains__ (in) for a Python class为 Python 类实现 __contains__ (in) 时出错
【发布时间】:2015-05-06 15:27:01
【问题描述】:

我正在尝试实现 contains 以便它可以用于我的 python 对象中的多个属性中的任何一个。我能够成功实现“==”和大多数其他比较运算符,但“in”给我带来了问题:

import operator
class Comparator:
     def __init__(self,fieldName,compareToValue,my_operator):
         self.op = my_operator
         self.field = fieldName
         self.comparedTo = compareToValue
     def __call__(self,row):
         my_row_val = getattr(row,self.field)
         return self.op(my_row_val,self.comparedTo)


class Row:
    class RowItem:
         def __init__(self,name):
              self.name = name
         def __eq__(self,other):
             return Comparator(self.name,other,operator.eq)
         def __contains__(self,other):
             return Comparator(self.name,other,operator.contains)
    val1 = RowItem("val1")
    val2 = RowItem("val2")
    val3 = RowItem("val3")
    val4 = RowItem("val4")
    def __init__(self, val1, val2, val3, val4):
        self.val1 = val1
        self.val2 = val2
        self.val3 = val3
        self.val4 = val4
    def __str__(self):
        return str([self.val1,self.val2,self.val3,self.val4])
    def __repr__(self):
        return str(self)


class MyTable:
    def __init__(self,rows):
        self.rows = rows
    def filter(self,condition):
        for row in self.rows:
            if condition(row):
               yield row

rows = [Row(1,2,3,"hello"),Row(1,2,7,"cat"),Row(1,2,3,"hi"),Row(7,7,7,"foo")]
mytable = MyTable(rows)

# the line below works fine!
print list(mytable.filter(Row.val3 == 7))

# this line below does not work
print list(mytable.filter("h" in Row.val4))
# TypeError: 'bool' object is not callable

# this line also does not work
print list(mytable.filter(Row.val4 in "hello world"))
# TypeError: 'in <string>' requires string as left operand, not instance

【问题讨论】:

  • __contains__ 根本不适合这项工作,放弃这个想法。你需要做一个可调用的传递给过滤器。
  • 您的过滤器方法需要一个可调用的“条件”对象。事实上,它试图为每一行调用它。但是,您传递的是一个布尔值!当您在 row_obj.val4 中写入“h”时,您正在计算单个布尔值,并且无法调用它。
  • 显然__eq__ 很乐意让你返回任何你喜欢的值,但__contains__ 会强制转换为布尔值。您返回的 Comparator 对象是真实的(大多数对象实例也是如此),因此 "h" in row_obj.val4 的计算结果为 True
  • 最后一行的问题是 RowItem.__contains__ 是在你想查看是否有 inRowItem 时调用的,而不是在你想查看 RowItem 是否在某物。 TypeError 是由 str.__contains__ 提出的,而不是你的 RowItem 班级。
  • 您可能对此感兴趣:Why is the return value of __contains__ coerced to boolean, but that of __lt__ and the like is not? 它讨论了一种可能的解决方法:重载 &lt;&lt; 运算符。

标签: python class contains


【解决方案1】:
  1. 对于filter,您必须传递一个可调用对象,而不是布尔值
  2. row_obj.val4RowItem 类的实例,而不是 string 类的 __contains__ 方法所期望的字符串

【讨论】:

  • Row.val3 == 7 怎么可能不是布尔值,而 "h" in Row.val4 怎么可能是布尔值?
  • 理论上,他可以编写一个 contains 方法,该方法将返回一个检查任何行是否包含的可调用对象。但是,这违反了协议:contains 方法应该返回一个布尔值。
  • 因为您的RowItem__eq__ 返回一个Comparator,这是可调用的
  • __contains__ for RowItem 也返回 Comparator
  • 你会喜欢它返回一个比较器,但似乎 Python 正在“有用地”为你将它转换为布尔值。
【解决方案2】:

感谢 Kevin 在 cmets 中回答这个问题。问题在于 in__contains__() 方法)将结果强制转换为布尔值,这与其他逻辑比较运算符(__lt__()__eq__() 等)不同。

这似乎主要是向后兼容性。 更多信息在这里: https://mail.python.org/pipermail/python-dev/2013-July/127297.html

解决此问题的一种方法是创建一个新方法(例如,contains_):

尝试这样的事情(这是一个不好的例子,因为 contains 可以在这段代码中工作:

import operator
class Comparator:
     def __init__(self,fieldName,compareToValue,my_operator):
         self.op = my_operator
         self.field = fieldName
         self.comparedTo = compareToValue
     def __call__(self,row):
         my_row_val = getattr(row,self.field)
         return self.op(my_row_val,self.comparedTo)


class Row:
    class RowItem:
         def __init__(self,name):
              self.name = name
         def __eq__(self,other):
             return Comparator(self.name,other,operator.eq)
         def contains_(self,other):
             return Comparator(self.name,other,operator.contains)
    val1 = RowItem("val1")
    val2 = RowItem("val2")
    val3 = RowItem("val3")
    val4 = RowItem("val4")
    def __init__(self, val1, val2, val3, val4):
        self.val1 = val1
        self.val2 = val2
        self.val3 = val3
        self.val4 = val4
    def __str__(self):
        return str([self.val1,self.val2,self.val3,self.val4])
    def __repr__(self):
        return str(self)

代替:

         def __contains__(self,other):
             return Comparator(self.name,other,operator.contains)

当然,在尝试执行“in”时,您需要执行以下操作:

print list(mytable.filter(Row.val4.contains_("h"))) #new way to call in (__contains__)

代替:

print list(mytable.filter(Row.val4.__contains__("h"))) #old broken way to call in (__contains__)

【讨论】:

    猜你喜欢
    • 2016-11-27
    • 2017-06-16
    • 2021-09-28
    • 2016-10-10
    • 1970-01-01
    • 1970-01-01
    • 2021-09-23
    • 2016-02-11
    • 2011-11-24
    相关资源
    最近更新 更多