6

I am trying to step through a list of lists operating on each element of each list using nested for loops. I am getting a warning from PyCharm that the type of the counter in the second for loop is not certain to be an integer, despite it is derived from a range value. The code executes correctly, by why the warning?

def get_vote_fraction(cl_count, ag_vector):
    v_f_vector = [[0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0, 0]]
    for b in range(0, len(v_f_vector)):
        for c in range(0, len(v_f_vector[b])):
            v_f_vector[b][c] = f"{(ag_vector[b][c] / cl_count): .2F}"
    return v_f_vector


aggregated_vector = [[0, 8, 0, 6], [0, 1, 0, 0, 0, 0, 9, 0], [0, 0, 10, 0], [0, 10, 0, 0, 0]]
class_count = 10
vote_fraction = get_vote_fraction(class_count, aggregated_vector)
print(vote_fraction)

As expected the output is [[' 0.00', ' 0.80', ' 0.00', ' 0.60'], [' 0.00', ' 0.10', ' 0.00', ' 0.00', ' 0.00', ' 0.00', ' 0.90', ' 0.00'], [' 0.00', ' 0.00', ' 1.00', ' 0.00'], [' 0.00', ' 1.00', ' 0.00', ' 0.00', ' 0.00']]

I get a warning that c in v_f_vector[b][c] can be of Unexpected type(s) (int, str) which says c is not sufficiently defined as an integer. I hesitate to raise an issue with PyCharm since I may be missing something simple. Anyone see what I am missing?

Pmason
  • 91
  • 4
  • `c` (your range variable) will indeed be an integer; if i see it right, the complaint is about `v_f_vector[b][c]` (the list element). –  Mar 26 '19 at 20:29
  • You can create a ticket. It is pretty common that Pycharm cannot resolve this kind of things from my experience. Just know that you can make the warning become a weak warning in your settings if it bothers you. – Eskapp Mar 26 '19 at 20:33
  • There was no warning for me when I tested this – Nipun Thennakoon Mar 26 '19 at 20:37

3 Answers3

2

Regarding the PyCharm warning (that the question is all about)

Although the 2nd index (c) is highlighted, making users think it is the problem, it's not about that, at all.

Generally, lists are homogeneous containers, meaning that they contain elements of the same type.
PyCharm evaluated v_f_vector as being a list of lists of ints (and thus v_f_vector[b][c] as an int). Trying to assign a string to it, would kind of break the list homogeneity, hence the warning.
Try assigning dummy values (e.g. 0.01f, (), [], ...) to it, and the warning text will change, like in the image below:

Img0

As a quick (and dirty) workaround, if the lists are going to contain strings, you could do something like:

v_f_vector = [["", "", "", ""], ["", "", "", "", "", "", "", ""], ["", "", "", ""], ["", "", "", "", ""]]

Regarding the code that lead to this

Declaring a list with a certain content, and then coming back and changing that content (based on another (similar structural) list) is counter-intuitive.

Your (end) goal, can be achieved in one line of code (the most Pythonic form), consisting of a list comprehension (check [Python 3.Docs]: List Comprehensions). Since your list nesting level is 2, so will be the comprehension:

>>> aggregated_vector = [[0, 8, 0, 6], [0, 1, 0, 0, 0, 0, 9, 0], [0, 0, 10, 0], [0, 10, 0, 0, 0]]
>>> class_count = 10
>>>
>>> vote_fraction = [[f"{(item1 / class_count): .2F}" for item1 in item0] for item0 in aggregated_vector]
>>> print(vote_fraction)
[[' 0.00', ' 0.80', ' 0.00', ' 0.60'], [' 0.00', ' 0.10', ' 0.00', ' 0.00', ' 0.00', ' 0.00', ' 0.90', ' 0.00'], [' 0.00', ' 0.00', ' 1.00', ' 0.00'], [' 0.00', ' 1.00', ' 0.00', ' 0.00', ' 0.00']]
>>>
>>> # Or, if you REALLY need to have a function
...
>>> def get_vote_fraction(cl_count, ag_vector):
...     return [[f"{(item1 / cl_count): .2F}" for item1 in item0] for item0 in ag_vector]
...
>>>
>>> print(get_vote_fraction(class_count, aggregated_vector))
[[' 0.00', ' 0.80', ' 0.00', ' 0.60'], [' 0.00', ' 0.10', ' 0.00', ' 0.00', ' 0.00', ' 0.00', ' 0.90', ' 0.00'], [' 0.00', ' 0.00', ' 1.00', ' 0.00'], [' 0.00', ' 1.00', ' 0.00', ' 0.00', ' 0.00']]

Note: your code could be adapted as well, but I'd suggest to go with the recommended variant(s).

CristiFati
  • 38,250
  • 9
  • 50
  • 87
1

I think that the warning comes from the fact that you are replacing integer values with strings. When I tried to instanciate v_f_vector with '0' instead of 0, PyCharm didn't show any warning:

v_f_vector = [['0', '0', '0', '0'], ['0', '0', '0', '0', '0', '0', '0', '0'], ['0', '0', '0', '0'], ['0', '0', '0', '0', '0']]

However, it may be not the best way to fix the problem. I would suggest such a refactoring of your code:

def get_vote_fraction(cl_count, ag_vector):
    v_f_vector = []
    for b in range(0, len(ag_vector)):
        v_f_vector.append([])
        for c in range(0, len(ag_vector[b])):
            v_f_vector[b].append(f"{(ag_vector[b][c] / cl_count): .2F}")
    return v_f_vector


aggregated_vector = [[0, 8, 0, 6], [0, 1, 0, 0, 0, 0, 9, 0], [0, 0, 10, 0], [0, 10, 0, 0, 0]]
class_count = 10
vote_fraction = get_vote_fraction(class_count, aggregated_vector)
print(vote_fraction)

It will allow you to change aggregated_vector without reflecting these changes to instanciated v_f_vector.

sanyassh
  • 8,100
  • 13
  • 36
  • 70
  • Thank you for the clearest answer to my question. I am aware of options like enumerate and list comprehensions! The snippet of code i provided was the simplest I could devise that created the warning situation, which is indeed exactly as you described. (I also like your refactoring suggestion!) – Pmason Mar 27 '19 at 16:09
0

You should use enumerate()

Example:

iterable = [1,2,3,4]
for i, v in enumerate(iterable):
    print(i,v)

Expected output

0 1
1 2
2 3
3 4

Instead of

for x in range(0, len(iterable)):
    print(x, iterable[x])

Sorry this isn't really an answer to your question, but it's for more readable this way, also, enumerate will always give you an index and a value, or if it was a dictionary then the key and the value.

It might also be worth noting it makes far more human readable sense to nest lists in a dictionary and then give each of them a specific key so that you can access them.

Just food for thought

To add clarity, let's say you have 4 vectors, vectors a, b, x and y. You could have each of their respective lists in their own dictionary key, then access them with their key.

Swift
  • 1,663
  • 1
  • 10
  • 21
  • 1
    If you understand this is not really an answer, why not adding a comment instead of an answer? – sanyassh Mar 27 '19 at 08:14
  • Because it's awful practice to use a range in len of an iterable. A comment cannot explain that as well as I have done with an example. – Swift Mar 27 '19 at 10:22