我正在使用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)。
就好像是在计数列表中的类出现,而不是对象。
答案 0 :(得分:0)
首先让我们了解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
,{ {1}},Py_NE
或Py_GT
,对应于Py_GE
,<
,<=
,==
,!=
,或>
。错误时返回>=
,如果结果为假,则返回-1
,否则返回0
。
因此,如果该值为1
(即1
),则计数器将递增。迭代之后,计数器将返回给调用者。
true
在CPython中大致等同于python层中的以下内容,
list_count
现在让我们回到您的问题。
虽然在测试代码中效果很好,但当 计数类继承了flask_login UserMixin类。
让我们学习一个示例类(不继承自def list_count(list_, item_to_count):
counter = 0
for iterm in list_:
if item == item_to_count:
counter += 1
return counter
)
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))
。但是python如何在这里执行比较???默认情况下,python会将1
的{{1}}(即对象的内存地址)与id
,p1
,p1
的ID进行比较。由于每个新对象都有不同的ID,因此count方法将返回p2
。
好吧,如果我们想在姓名相等的情况下将该人算作一个人,该怎么办?
让我们举个例子。
p3
但是这仍然返回1
,因为python仍在与其对象ID进行比较。那么我该如何自定义呢?您可以覆盖对象的p1 = Person("Person1")
p2 = Person("Person1")
print([p1, p2].count(p1)) # I want this to be return 2
。即,
1
现在它按预期返回__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__
方法。
__eq__
如您所见,比较是基于其UserMixin
属性执行的。在这种情况下,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
属性,因此对于所有实例它都是相同的。
如何解决此问题,
从逻辑角度看,每个对象都有唯一的ID。因此Element
应该是实例级别的属性。请参见id
代码库本身的一个示例。
id
答案 1 :(得分:0)
这个“ id”问题是关键。
返回sqlalchemy上下文,该列表包含ID为主键的对象...所有对象首先都设置为“无”。
并且只有在最终修复session.add()和session.commit()之后才会进行更新。
谢谢。