我可以更改 Python 字典中键的比较方式吗?我想使用运算符 'is' 而不是 ==

Can I change the way keys are compared in a Python dict? I want to use the operator 'is' instead of ==

假设我有两个对象 class:objA 和 objB。他们的关系如下:

(objA == objB)    #true
(objA is objB)    #false

如果我将两个对象都用作 Python 字典中的键,那么它们将被视为相同的键,并相互覆盖。有没有办法覆盖字典比较器以使用 is 比较而不是 == 以便这两个对象将被视为字典中的不同键?

也许我可以重写 class 中的 equals 方法之类的?更具体地说,我说的是 BeautifulSoup4 库中的两个 Tag 对象。

这是我所说的更具体的例子:

from bs4 import BeautifulSoup

HTML_string = "<html><h1>some_header</h1><h1>some_header</h1></html>"

HTML_soup = BeautifulSoup(HTML_string, 'lxml')

first_h1 = HTML_soup.find_all('h1')[0]      #first_h1 = <h1>some_header</h1>
second_h1 = HTML_soup.find_all('h1')[1]     #second_h1 = <h1>some_header</h1>

print(first_h1 == second_h1)        # this prints True
print(first_h1 is second_h1)        # this prints False

my_dict = {}
my_dict[first_h1] = 1
my_dict[second_h1] = 1

print(len(my_dict))                 # my dict has only 1 entry!

# I want to have 2 entries in my_dict: one for key 'first_h1', one for key 'second_h1'.

您似乎想覆盖运算符 ==,您可以选择构建新 class 的选项并实施运算符 ==:

def  __eq__(self,  obj) :
      return (self is obj) 

first_h1second_h1Tag class 个实例 。当您执行 my_dict[first_h1]my_dict[second_h1] 时,标签的 字符串表示形式 用于散列。问题是,这两个 Tag 实例都具有相同的字符串表示形式:

<h1>some_header</h1>

这是因为Tagclass有__hash__()魔术方法定义如下:

def __hash__(self):
    return str(self).__hash__()

其中一个解决方法是使用 id() 值作为散列,但是在 BeautifulSoup 本身内部重新定义 Tag classes 存在问题.您可以通过自定义 "tag wrapper":

来解决该问题
class TagWrapper:
    def __init__(self, tag):
        self.tag = tag

    def __hash__(self):
        return id(self.tag)

    def __str__(self):
        return str(self.tag)

    def __repr__(self):
        return str(self.tag)

然后,您将能够做到:

In [1]: from bs4 import BeautifulSoup
   ...: 

In [2]: class TagWrapper:
   ...:     def __init__(self, tag):
   ...:         self.tag = tag
   ...: 
   ...:     def __hash__(self):
   ...:         return id(self.tag)
   ...: 
   ...:     def __str__(self):
   ...:         return str(self.tag)
   ...: 
   ...:     def __repr__(self):
   ...:         return str(self.tag)
   ...:     

In [3]: HTML_string = "<html><h1>some_header</h1><h1>some_header</h1></html>"
   ...: 
   ...: HTML_soup = BeautifulSoup(HTML_string, 'lxml')
   ...: 

In [4]: first_h1 = HTML_soup.find_all('h1')[0]      #first_h1 = <h1>some_header</h1>
   ...: second_h1 = HTML_soup.find_all('h1')[1]     #second_h1 = <h1>some_header</h1>
   ...: 

In [5]: my_dict = {}
   ...: my_dict[TagWrapper(first_h1)] = 1
   ...: my_dict[TagWrapper(second_h1)] = 1
   ...: 
   ...: print(my_dict)
   ...: 
{<h1>some_header</h1>: 1, <h1>some_header</h1>: 1}

不过,它不漂亮而且使用起来不太方便。我会重申你最初的问题,并检查你是否真的需要将标签放入字典中。

你也可以使用 Python 的自省能力来猴子修补 bs4,比如 ,但这将进入一个相当危险的领域。