0

I have a school assignment, and therefore we're required to use PyCharm and follow the PEP8 and Google standards regarding Python3. While my code works perfectly, PyCharm claims the index of rows[0] is of an unexpected value.

def create_matrix():
"""
    Function made to create a matrix of who wins or who loses based on the number of figures in a game.
    Keep in mind the order of the figures tuple. first element wins over second element, last element wins over
    first element. an element also wins against the element two steps before it, in the case of five elements.
    i haven't researched what relationships exist in higher dimensions, but there will always be an equal amount of
    elements who beat you, as elements you beat. the missing element will be a draw.
"""
figures = ("Scissors", "Paper", "Rock", "Lizard", "Spock")  # caution with order of elements
number_of_figures = len(figures)  # expected to be an odd number
half_set = number_of_figures // 2  # half_set = int((n-1)/2)

win_and_lose = [+1, -1]  # negative: row (player_b), positive: column (player_a), zero: draw
rows = [[None]*number_of_figures]*number_of_figures  # uses same size in memory as a "full" set

rows[0] = [0] + win_and_lose*half_set  # [0, -1, +1, -1, +1...]

for row in range(1, number_of_figures):
    rows[row] = rotate(rows[row-1], 1)

# for row in range(0, len(rows)):
#    print(rows[row])
return figures, rows

Linter warning

I have tested changing the specific line which is giving me a lint warning to

    rows[0][0], rows[0][1], rows[0][2], rows[0][3], rows[0][4] = [0] + win_and_lose*half_set  # [0, -1, +1, -1, +1...]

and the code works without any lint warning. The problem I have is that I want the tuple to be expandable in size. I know I can do this with a for loop along the lines of

    for column in range(0, number_of_figures):
    if column == 0:
        rows[0][column] = 0
    elif column % 2 == 0:
        rows[0][column] = +1
    elif column % 2 == 1:
        rows[0][column] = -1
    else:
        raise Exception("Unexpected column value")

but I have some issues with this:

1) It isn't readable at all for someone who wants to look at the code.
2) When I try to print the list now, it has set all rows to [0, -1, +1, +1, -1], and not only the first element, as I expect it to do. This doesn't cause any runtime errors, but it's not what I want.

My questions is how I can write this piece code in a pythonic and easy to read manner, without breaking any Python standard.

Bonus question: I want to minify the amount of memory usage of my code, and I've seen through __sizeof__() that if rows contains five elements with value None, it uses 80 bytes of memory. Same size is used if I have five elements with value [None, None, None, None, None]. The size remains unchanged once I set some values to the matrix. Any tips regarding this? I've set up the rotate() function so that I can rotate the list n positions at a time, so I could skip to any given row having the first row defined.

mazunki
  • 682
  • 5
  • 17
  • What's the linter warning? – Adam Smith Nov 26 '18 at 08:34
  • 2
    `[[None]*number_of_figures]*number_of_figures` does not create `number_of_figures` different rows. It creates a list of `number_of_figures` references to the same row. That is why changing one row changes all other rows, too. – DYZ Nov 26 '18 at 08:40
  • 1
    @AdamSmith I added a screenshot. – mazunki Nov 26 '18 at 08:43
  • Ah, I understand, @DYZ . Why does it work in the first case: `rows[0] = [0] + win_and_lose*half_set` ? – mazunki Nov 26 '18 at 08:45
  • 1
    You may find [this](https://stackoverflow.com/questions/240178/list-of-lists-changes-reflected-across-sublists-unexpectedly) useful. – DYZ Nov 26 '18 at 08:48

2 Answers2

2

PyCharm is complaining because you initialize rows as a list of list of Nones, then re-assign it to be a list of list of ints. Regardless it's a linter warning you are safe to ignore, since dynamic typing is something Python is supposed to do (if you want static typing, go write in a static language).

You could fix it by simply initializing as:

rows = [[0 for _ in range(number_of_figures)] for _ in range(number_of_figures)]
# this is identical to `[[0] * number_of_figures] * number_of_figures` except it
# avoids the duplication of references issue you were experiencing that DYZ
# pointed out in the comments above.
Adam Smith
  • 52,157
  • 12
  • 73
  • 112
  • Oh, this fixed the linter warning. I understand why it complained now, but it wouldn't be runtime-safe to set it to zero, as that's my default draw situation. I can change this to some other number, but I'd prefer to avoid it. Can I somehow tell it to ignore the type-casting from None-type to Integer? – mazunki Nov 26 '18 at 08:56
  • 1
    @mazunki it's safe to ignore the linter warning entirely. Linters can complain -- that's allowed -- sometimes lint doesn't indicate bad code. – Adam Smith Nov 26 '18 at 08:57
  • 1
    @mazunki also: you should look at `collections.deque`. It's a cute way to sidestep this whole problem. – Adam Smith Nov 26 '18 at 08:58
  • Personally I only care about code working too, but school has all sorts of requirements for grading, I guess. It's a nice tip and reminder, anyway. I'll take a look at collection deques. – mazunki Nov 26 '18 at 09:10
  • 1
    @mazunki see [this sample code](https://gist.github.com/NotTheEconomist/538cd78dd6cf5c1146795e8767bd1ae7) – Adam Smith Nov 26 '18 at 09:14
0

Assuming the comment by DYZ above isn't existent in the first solution, you can use type hinting to help the linter understand the type of rows. In my code this eliminates the linter warning.

rows = [[None]*number_of_figures]*number_of_figures  # type: list #uses same size in memory as a "full" set

This syntax is also available for python 3:

rows:list = [[None]*number_of_figures]*number_of_figures
Shushan
  • 1,235
  • 1
  • 10
  • 15