UserMixin继承干扰python list.count()方法

时间:2019-01-11 12:44:03

标签: python sqlalchemy

我正在使用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)。

就好像是在计数列表中的类出现,而不是对象。

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指定的操作比较o2opid的值,该操作必须是Py_LTPy_LEPy_EQ,{ {1}},Py_NEPy_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}}(即对象的内存地址)与idp1p1的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()之后才会进行更新。

谢谢。