我正在使用 list.count() 方法来检查关系是否具有元素。
虽然它在测试代码中工作得很好,但当计数的类继承flask_login UserMixin 类时,它就不再起作用了。
为什么,以及如何解决它?
class Element(UserMixin):
id=1
name="default"
def __init__(self, name):
name=name
elementsList=[]
elt1=Element(name="1")
elt2=Element(name="2")
elt3=Element(name="3")
elementsList.append(elt1)
elementsList.append(elt2)
print("Counting Element2 should return 1: ", elementsList.count(elt2)) # returns 2
print("Counting Element3 should return 0: ", elementsList.count(elt3)) # returns 2
我应该获取列表中的元素数量(1 或 0)。
相反,我得到了整个列表的长度(2,即使我附加了更多整数)。
就好像它正在计算列表中类的出现次数,而不是对象。
最佳答案
首先让我们了解如何list.count
作品。从cpython源代码list.count
具有以下定义。
static PyObject *
list_count(PyListObject *self, PyObject *value)
{
Py_ssize_t count = 0;
Py_ssize_t i;
for (i = 0; i < Py_SIZE(self); i++) {
int cmp = PyObject_RichCompareBool(self->ob_item[i], value, Py_EQ);
if (cmp > 0)
count++;
else if (cmp < 0)
return NULL;
}
return PyLong_FromSsize_t(count);
}
所以当你执行some_list.count(some_element)
时, Python will iterate over every object in the list, and perform a rich comparison (即 PyObject_RichCompareBool
)。
来自C-API文档丰富的比较(即 PyObject_RichCompareBool(PyObject *o1, PyObject *o2, int opid)
)将
比较 o1
的值和o2
使用 opid
指定的操作,必须是 Py_LT
之一, Py_LE
, Py_EQ
, Py_NE
, Py_GT
,或Py_GE
,对应<
, <=
, ==
, !=
, >
,或>=
分别。返回-1
出错时,0
如果结果为假,1
否则。
如果值为 1
(即 true
)计数器将增加。迭代结束后,计数器将返回给调用者。
list_count
在CPython中大致相当于Python层中的以下内容,
def list_count(list_, item_to_count):
counter = 0
for iterm in list_:
if item == item_to_count:
counter += 1
return counter
现在让我们回到你的问题。
While it works pretty well in a test code, it doesnt anymore when the counted class inherits the flask_login UserMixin class.
让我们看一个示例类(不继承 UserMixin
)
class Person
def __init__(self, name):
self.name = name
p1 = Person("Person1")
p2 = Person("Person2")
p3 = Person("Person3")
print([p1, p2, p3].count(p1))
这将打印 1
正如我们所预期的。但是python这里是如何进行比较的呢???默认情况下,python 将比较 id
p1
(即对象的内存地址) ID 为 p1
, p2
, p3
。由于每个新对象都有不同的 id ,因此 count 方法将返回 1
.
好吧,如果名字相等的话我们想把这个人算作一个怎么办???
让我们举同样的例子。
p1 = Person("Person1")
p2 = Person("Person1")
print([p1, p2].count(p1)) # I want this to be return 2
但这仍然返回1
因为 python 仍在与它的对象 ID 进行比较。那么我该如何定制呢?您可以覆盖 __eq__
的对象。即,
class Person(object):
def __init__(self, name):
self.name = name
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.name == other.name
return NotImplemented
p1 = Person("Person1")
p2 = Person("Person1")
print([p1, p2].count(p1))
哇现在它回来了2
正如预期的那样。
现在让我们考虑继承自 UserMixin
的类.
class Element(UserMixin):
id=1
def __init__(self, name):
self.name=name
elementsList=[]
elt1=Element(name="1")
elt2=Element(name="2")
elt3=Element(name="3")
elementsList.append(elt1)
elementsList.append(elt2)
print(elementsList.count(elt2))
这将打印 2
。为什么?。如果根据 ids
进行比较本来应该是 1
。所以就会有__eq__
在某处实现。因此,如果您查看 UserMixin
类实现它实现 __eq__
方法。
def __eq__(self, other):
'''
Checks the equality of two `UserMixin` objects using `get_id`.
'''
if isinstance(other, UserMixin):
return self.get_id() == other.get_id()
return NotImplemented
def get_id(self):
try:
return text_type(self.id)
except AttributeError:
raise NotImplementedError('No `id` attribute - override `get_id`')
如您所见,比较是根据其 id
进行的。属性。在这种情况下Element
类设置id
类级别的属性,因此对于所有实例来说都是相同的。
How to fix this,
从逻辑角度来看,每个对象都有唯一的 id。因此id
应该是实例级属性。请参阅 flask-login
中的一个示例代码库本身。
class User(UserMixin):
def __init__(self, name, id, active=True):
self.id = id
self.name = name
self.active = active
def get_id(self):
return self.id
@property
def is_active(self):
return self.active
关于python - UserMixin 继承干扰了 python list.count() 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54146830/