python - UserMixin 继承扰乱了python list.count() 方法
问题描述
我正在使用 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 将遍历列表中的每个对象,并执行丰富的比较(即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
现在让我们回到你的问题。
虽然它在测试代码中运行良好,但当计数类继承flask_login UserMixin 类时,它就不再适用了。
让我们举一个示例类(不继承自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 进行比较。由于每个新对象都有不同的 id,count 方法将返回。p1
p2
p3
1
好的,那么如果我们想在名字相同的情况下将这个人算作一个呢???
让我们举同样的例子。
p1 = Person("Person1")
p2 = Person("Person1")
print([p1, p2].count(p1)) # I want this to be return 2
但是这仍然返回1
,因为 python 仍然与其对象 ID 进行比较。那么我该如何定制呢?您可以覆盖__eq__
对象。IE,
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
属性,因此所有实例都相同。
如何解决这个问题,
从逻辑的角度来看,每个对象都有唯一的 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 - 我一直在 Swift 中使用一个具有出色返回闭包的函数
- python - Tensorflow:将未知大小的张量拆分为给定大小的块
- javascript - 如何在express js中将多个请求合并为一个请求对象?
- sequelize.js - SequelizeAssociationError:您在两个单独的关联中使用了别名。别名关联必须具有唯一别名
- security - 阻止从 Cloudfront 访问的 S3 上的公共访问?
- python-3.x - 如何使用python脚本调用带有输入参数的批处理文件
- html - 如何在 BI 报表中嵌入 HTML 内容以实现突发?
- python - 在后台运行 mstsc
- jenkins - 尝试通过 Jenkins 管道中的 curl 请求将文件上传到共享点时,Curl 28 超时
- firebase - 使用新的 Pub/Sub 功能的 Firestore 自动备份