我有一个像这样定义的简单类:
class User(object):
def __init__(self, id=None, name=None):
self.id = id
self.name = name
def __contains__(self, item):
return item == self.id
使用这个类,我可以对类的单个实例进行简单的检查,如下所示:
>>> user1 = User(1, 'name_1')
>>> 1 in user1
True
>>> 2 in user1
False
这是按预期工作的。
如何查看某个值是否在User
个对象列表中?它总是似乎返回False。
示例:
from random import randint
from pprint import pprint
users = [User(x, 'name_{}'.format(x)) for x in xrange(5)]
pprint(users, indent=4)
for x in xrange(5):
i = randint(2,6)
if i in users:
print("User already exists: {}".format(i))
else:
print("{} is not in users list. Creating new user with id: {}".format(i, i))
users.append(User(i, 'new_user{}'.format(i)))
pprint(users, indent=4)
这会创建类似于此的输出:
[ 0 => name_0,
1 => name_1,
2 => name_2,
3 => name_3,
4 => name_4]
6 is not in users list. Creating new user with id: 6
6 is not in users list. Creating new user with id: 6
6 is not in users list. Creating new user with id: 6
3 is not in users list. Creating new user with id: 3
3 is not in users list. Creating new user with id: 3
[ 0 => name_0,
1 => name_1,
2 => name_2,
3 => name_3,
4 => name_4,
6 => new_user6,
6 => new_user6,
6 => new_user6,
3 => new_user3,
3 => new_user3]
问题是身份6
的用户应该只创建一次,因为它尚未创建。尝试6
的第二次和第三次,它应该失败。用户身份标识3
根本不应该重新创建,因为它是users
变量初始化的一部分。
如何与__contains__
方法进行修改,以便在与我的班级的多个实例进行比较时能够正确使用in
?
答案 0 :(得分:4)
如果users
是用户列表,并且您选中了if i in user
,那么您不会检查User.__contains__
。您正在查看list.__contains__
。无论您在User.__contains__
中执行什么操作都不会影响检查i
是否在列表中的结果。
如果您想检查i
是否与User
中的users
匹配,您可以执行以下操作:
if any(i in u for u in users)
或者更清楚一点:
if any(u.id==i for u in users)
并且完全避免使用User.__contains__
。
答案 1 :(得分:4)
这是对__contains__
的误用。您希望在__contains__
等类上实施UserList
。
更好的方法是直接在生成器表达式或列表推导中访问id
属性(而不是使用in运算符)。例如
class User(object):
def __init__(self, id=None, name=None):
self.id = id
self.name = name
user = User(1, 'name_1')
assert 1 == user.id
user_list = [user, User(2, 'name_2')]
assert any(2 == u.id for u in user_list)
然后,对于您的随机示例,您将使用集合或字典来存储已存在的用户的ID。
users = [User(x, 'name_{}'.format(x)) for x in xrange(5)]
ids = set(u.id for u in users)
for x in xrange(5):
i = randint(2,6)
if i in ids:
print("User id already exists: {}".format(i))
else:
print("{} is not in users list. Creating new user with id: {}".format(i, i))
ids.add(i)
users.append(User(i, 'new_user{}'.format(i)))
答案 2 :(得分:2)
这似乎是您真正想要定义__eq__
以接受与其他User
个对象和int
的比较的情况。这将使包含对User
集合的检查自动生效,并且在一般用法中比在非容器类型上实现__contains__
更有意义。
from operator import index
class User(object):
def __init__(self, id=None, name=None):
self.id = id
self.name = name
def __eq__(self, item):
if isinstance(item, User):
return self.id == item.id and self.name == item.name
try:
# Accept any int-like thing
return self.id == index(item)
except TypeError:
return NotImplemented
def __ne__(self, item): # Only needed on Py2; Py3 defines it implicitly
ret = self == item
if ret is NotImplemented: return ret
return not ret
def __hash__(self):
return self.id
现在,您可以将您的类型与普通集合(包括set
和dict
键)一起使用,并且可以轻松查找。
from operator import attrgetter
# Use set for faster lookup; can sort for display when needed
users = {User(x, 'name_{}'.format(x)) for x in xrange(5)}
pprint(sorted(users, key=attrgetter('id')), indent=4)
for x in xrange(5):
i = randint(2,6)
if i in users:
print("User already exists: {}".format(i))
else:
print("{} is not in users list. Creating new user with id: {}".format(i, i))
users.add(User(i, 'new_user{}'.format(i)))
pprint(sorted(users, key=attrgetter('id')), indent=4)