检查项目是否已经在具有引用相等性的列表中

Check if item is already in a list with referential equality

我想检查某个项目是否已经在具有引用相等性而非结构相等性的列表中。

为清楚起见:

referential equality between 2 items is checked with item1 is item2

structural equality is checked with item1 == item2

检查一个项目是否已经在列表中的结构相等很容易像这样完成:

item in list

所以我正在寻找具有引用相等性的等效行。如果不遍历列表中的每个项目,这可能吗?

需要如何实施的示例(仅供说明):

def get_all_items_in_list(items):
   all_items = []
   for item in items:
       if not item in all_items:   # replace with referential equality here
           all_items.append(item)
   return all_items

# setup
a = (1, [])
b = (1, [])
print(a is b)    # prints False
items = [a, a, b]
print(get_all_items_in_list(items))  # should print [(1, []), (1, [])] but prints [(1, [])]

试试这个,

a=10
b=20
c=30
l=[id(a),id(b),id(c)]
id(a) in l

因此,根据 "is" python's operator,它检查对象的 identity,这可以通过 id() 函数实现(进一步滚动以获取解决方案).

# as you did
a = (1, [])
b = (1, []) # two different objects
print(a is b)
print(id(a), id(b))
>>> False
>>> 2076884715144 2076884729224

# Now let's try making the second object the same
c = (2, [])
d = c #objects are the same
print(c is d)
print(id(c), id(d))
>>> True
>>> 2076899815240 2076899815240

解决方案:

下面的想法是获取另一个列表中与原始列表位置相同的所有ids()。检查项目是否在列表中时,您将检查身份是否已经在列表中。

def get_ids_items_in_list(items):
    """ Get the identity of the objects in same order as list"""
    items_ids = []
    for item in items:
        items_ids.append(id(item))
    return items_ids

def get_all_items_in_list(items_list):
    # get our ids
    items_ids = get_ids_items_in_list(items_list)

    all_items = []
    all_items_id = []

    # Loops over the ids but using enumerate to get positions
    for idx, item in enumerate(items_ids):
        if not item in all_items_id:
            all_items_id.append(item)
            # if id is in list, append object according to position
            all_items.append(items_list[idx])
    return all_items

# setup
a = (1, [])
b = (1, [])
print(a is b)    # prints False

items = [a, a, b]
print(get_all_items_in_list(items))

>>> False
>>> [(1, []), (1, [])]

关于该主题的更多信息:

# another thing to notice is that a simple copy()
# will return the same object
from copy import copy
e = (3, [])
f = copy(e)
print(e is f)
print(id(e), id(f))
>>> True
>>> 2076884710600 2076884710600

# but a deepcopy will change the identity
from copy import deepcopy
g = (4, [])
h = deepcopy(g)
print(g is h)
print(id(g), id(h))
>>> False
>>> 2076884714120 2076884715016

您可以使用由项目 id 键入的中间 dict 作为标识集:

def get_all_items_in_list(items):
   return {id(it): it for it in items}.values()

# arrange
a = (1, [])
b = (1, [])
items = [a, a, b]

# act
result = list(get_all_items_in_list(items))

# assert
print(result[0] is a)
print(result[1] is b)

如果没有 一些 循环,你将无法做到这一点。 in 的 python 中没有检查身份的等效运算符。一个简短的解决方案可能如下所示:

l = [1,2,3]
l2 = [[1, 2, 3], [4, 5, 6]]
l3 = [[1, 2, 3], [4, 5, 6], l]

any(l is sub for sub in l2)
# false

any(l is sub for sub in l3)
# True

它仍然循环,但如果找到真值,它会提前退出。

如果您真的需要为此使用运算符,您可以子类化UserList并覆盖__contains__()。这将改变 in:

的行为
from collections import UserList

class identList(UserList):
    def __contains__(self, other):
        return any(other is sub for sub in self)


l = [1,2,3]
l2 = identList([[1, 2, 3], [4, 5, 6]])
l3 = identList([[1, 2, 3], [4, 5, 6], l])

l in l2
# False

l in l3
# True
#With minimal changes in existing code snippet
def get_all_items_in_list(items):
   all_items = {}
   for item in items:
       if not id(item) in all_items:   # replace with referential equality here
           all_items[id(item)] = item
   return all_items.values()

# setup
a = (1, [])
b = (1, [])
print(a is b)    # prints False
items = [a, a, b]
print(get_all_items_in_list(items))

输出:

False

dict_values([(1, []), (1, [])])