a.count(0)
always returns 11, so what should I do to discount the False
and return 10?
a = ["a",0,0,"b",None,"c","d",0,1,False,0,1,0,3,[],0,1,9,0,0,{},0,0,9]
a.count(0)
always returns 11, so what should I do to discount the False
and return 10?
a = ["a",0,0,"b",None,"c","d",0,1,False,0,1,0,3,[],0,1,9,0,0,{},0,0,9]
Python 2.x interprets False
as 0
and vice versa. AFAIK even None
and ""
can be considered False
in conditions.
Redefine count as follows:
sum(1 for item in a if item == 0 and type(item) == int)
or (Thanks to Kevin, and Bakuriu for their comments):
sum(1 for item in a if item == 0 and type(item) is type(0))
or as suggested by ozgur in comments (which is not recommended and is considered wrong, see this), simply:
sum(1 for item in a if item is 0)
it may (“is” operator behaves unexpectedly with integers) work for small primary types, but if your list contains objects, please consider what is
operator does:
From the documentation for the is
operator:
The operators
is
andis not
test for object identity:x is y
is true if and only if x and y are the same object.
More information about is
operator: Understanding Python's "is" operator
This can be done with sum
and a generator expression that compares each element to False
:
>>> a = ["a",0,0,"b",None,"c","d",0,1,False,0,1,0,3,[],0,1,9,0,0,{},0,0,9]
>>> sum((x == 0 and x is not False) for x in a)
10
This works because bool
is a subclass of int
- False
equals 0 and True
equals 1.
You need to filter out the Falses yourself.
>>> a = ["a",0,0,"b",None,"c","d",0,1,False,0,1,0,3,[],0,1,9,0,0,{},0,0,9]
>>> len([x for x in a if x == 0 and x is not False])
10
Old answer is CPython specific and it's better to use solutions that work on all Python implementations.
Since CPython keeps a pool of small integer objects, zero included, you can filter the list with the is
operator.
This of course shouldn't be used for value comparisons but in this case it works since zeros are what we want to find.
>>> a = ["a",0,0,"b",None,"c","d",0,1,False,0,1,0,3,[],0,1,9,0,0,{},0,0,9]
>>> [x for x in a if x is 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
>>> len(_)
10
How about this functional style solution:
print zip(a, map(type, a)).count((0, int))
>>> 10
Timing this against some of the other answers here, this also seems to be one of the quickest:
t0 = time.time()
for i in range(100000):
zip(a, map(type, a)).count((0, int))
print time.time() - t0
>>> 0.275855064392
t0 = time.time()
for i in range(100000):
sum(1 for item in a if item == 0 and type(item) == int)
print time.time() - t0
>>> 0.478030204773
t0 = time.time()
for i in range(100000):
sum(1 for item in a if item == 0 and type(item) is type(0))
print time.time() - t0
>>> 0.52236700058
t0 = time.time()
for i in range(100000):
sum((x==0 and x is not False) for x in a)
print time.time() - t0
>>> 0.450266122818
Solving this problem more generally, you could make your own subclass of list:
class key_equality_list(list):
def __init__(self, key, items):
super(key_equality_list, self).__init__(items)
self._f = key
def __contains__(self, x):
return any(self._f(x) == self._f(item) for item in self)
def count(self, x):
return sum(self._f(x) == self._f(item) for item in self)
And then define a key function that checks types:
type_and_val = lambda x: (x, type(x))
So your usage becomes:
>>> a = ["a",0,0,"b",None,"c","d",0,1,False,0,1,0,3,[],0,1,9,0,0,{},0,0,9]
>>> a = key_equality_list(type_and_val, a)
>>> a.count(0)
10
>>> a.count(False)
1
Another option is to first "decorate" the list by adding the types and the count:
decorated_seq = list(map(lambda x: (x, type(x)), sequence))
decorated_seq.count((0, type(0)))
If you want to have 0 == 0.0
you could do:
decorated_seq = list(map(lambda x: (x, isinstance(x, bool)), sequence))
decorated_seq.count((0, False))
Which counts all 0
s that are not of type bool
(i.e. False
s).
In this way you can define what count
should do in any way you want, at the cost of creating a temporary list.
Since count
returns the number of items equal to its input, and since 0
is equal to False
in Python, one way to approach this is to pass in something that is equal to 0
but is not equal to False
.
Python itself provides no such object, but we can write one:
class Zero:
def __eq__(self, other):
return other == 0 and other is not False
print([1, 2, 0, False].count(Zero()))
Be aware that there are other things in Python that, like False
, are equal to 0
. I can think of 0.0
, decimal.Decimal(0)
, and fractions.Fraction(0,1)
. Furthermore, anybody can subclass int
to create other "fake 0 values", just like False
. The code above counts all of these, treating False
as the only special case, but you might want to do something different in your __eq__
.
Of course "under the covers" this is very similar to just doing sum(other == 0 and other is not False for other in a)
. But considering how count()
is defined, it should come as no surprise that a.count(x)
is similar to sum(x == y for y in a)
.
You would only do this in the rare case where you have for some reason already decided to use count
(perhaps because you're using some third-party code that's going to call it), and need to come up with a suitable object to pass it. Basically, it treats count
as a function that calls an arbitrary function we write (requiring only that this function be the __eq__
function of the argument) and counts the number of true return values. This is an accurate description of what count
does, but not really the point of count
since sum
is more explicit.
Many appropriate answers already given. Of course, an alternative is to filter the list on the type of the element you are querying for first.
>>> a = ["a",0,0,"b",None,"c","d",0,1,False,0,1,0,3,[],0,1,9,0,0,{},0,0,9]
>>> def count(li, item):
... return list(filter(lambda x: type(x) == type(item), li)).count(item)
...
>>>
>>> count(a, 0)
10
>>> count(a, False)
1
>>> count(a, "a")
1
>>> class my_own_int(int): pass
...
>>> a.append(my_own_int(5))
>>> a.count(5)
1
>>> count(a, 5)
0
In case you are worried about the copy, you can use sum
and abuse the fact we are trying to circumvent above (that True == 1
).
def count(li, item):
return sum(map(lambda x: x == item, filter(lambda x: type(x) == type(item), li)))
If you run into that kind of problems frequently you can introduce the following class*
class Exactly:
def __init__(self, something):
self.value = something
def __eq__(self, other):
return type(self.value) == type(other) and self.value == other
and use it as follows:
a.count(Exactly(0))
* Of course you may need to enhance it for other operations too.