Home > Enterprise >  Sorting class based on attributes 'object is not subscriptable'
Sorting class based on attributes 'object is not subscriptable'

Time:07-31

I have almost finished my project for creating a scoreboard based on user input. However, when it comes to my last step, sorting based on the results given, my code does not seem to function. The output that I receive is as follows:

["player_result(name='Trees', points=0.5, resistance_points=2.0, sonnenborn_berger=0.5, black=1)", "player_result(name='Erik', points=1.0, resistance_points=1.5, sonnenborn_berger=0.75, black=1)", "player_result(name='Udo', points=1.0, resistance_points=2.0, sonnenborn_berger=1.0, black=1)", "player_result(name='Ronald', points=1.0, resistance_points=2.5, sonnenborn_berger=1.25, black=1)", "player_result(name='Truus', points=1.0, resistance_points=2.0, sonnenborn_berger=0.0, black=1)", "player_result(name='Omar', points=0.0, resistance_points=2.0, sonnenborn_berger=0, black=1)", "player_result(name='Cornelis', points=1.0, resistance_points=1.5, sonnenborn_berger=0.0, black=1)", "player_result(name='Ria', points=1.5, resistance_points=2.5, sonnenborn_berger=1.75, black=1)", "player_result(name='Otto', points=1.0, resistance_points=2.0, sonnenborn_berger=1.0, black=1)", "player_result(name='Emma', points=1.0, resistance_points=2.0, sonnenborn_berger=1.0, black=1)", "player_result(name='Henk', points=1.5, resistance_points=2.0, sonnenborn_berger=1.5, black=1)", "player_result(name='Ulrich', points=1.0, resistance_points=2.0, sonnenborn_berger=0.5, black=1)", "player_result(name='Cor', points=1.5, resistance_points=2.5, sonnenborn_berger=1.75, black=1)", "player_result(name='Piet', points=1.0, resistance_points=1.5, sonnenborn_berger=0.0, black=1)", "player_result(name='Theo', points=2.0, resistance_points=1.0, sonnenborn_berger=1.0, black=0)", "player_result(name='Thea', points=0.0, resistance_points=3.0, sonnenborn_berger=0, black=2)"]

What I would like to see is that these items are sorted from highest to lowest in the following order: points, resistance-points, sonnenborn-berger, black.

I have looked into the Sorting HOW TO documentation, on which I based the 2 sorted() lines in my code. However, the sorted(print_list) line gives no errors, but does not sort, and the sorted(return_list) line gives the following error.

Traceback (most recent call last):
  File "{file}", line 70, in <module>
    determine_output("""Trees
  File "{file}", line 65, in determine_output
    sorted_return_list = sorted(return_list, key=itemgetter(1, 2, 3, 4), reverse=True)
TypeError: 'player_result' object is not subscriptable

My question: how can I sort my class objects based on the attributes mentioned above? I am more 'concerned' that the return value is sorted as expected; the print value is to make the sorting 'visible to me'. For full clarity, my code is as follows:

from operator import itemgetter


class player_result:
    def __init__(self, name: str, points: float, resistance_points: float, sonnenborn_berger: float, black: int):
        self.name = name
        self.points = points
        self.resistance_points = resistance_points
        self.sonnenborn_berger = sonnenborn_berger
        self.black = black

    def __str__(self):
        return 'player_result(name=\''   self.name   '\', points='   str(self.points)   ', resistance_points='   \
               str(self.resistance_points)   ', sonnenborn_berger='   str(self.sonnenborn_berger)   \
               ', black='   str(self.black)   ')'

    def __eq__(self, other):
        return self.name == other.name and self.points == other.points and \
               self.resistance_points == other.resistance_points and \
               self.sonnenborn_berger == other.sonnenborn_berger and self.black == other.black


def determine_output(input: str):
    # splitten van lijnen
    splits = input.split("\n\n")
    rounds = [i.split('\n') for i in splits]
    # bepalen van players
    players = {}
    for name in rounds[0]:
        players[name] = player_result(name, 0, 0, 0, 0)
    # bepalen van points van iedere player
    for one_round in rounds[1:]:
        for game in one_round:
            white_player, black_player, result_white_player, result_black_player = game.split()
            players[white_player].points  = float(result_white_player)
            players[black_player].points  = float(result_black_player)
            players[black_player].black  = 1
    # bepalen van resistance_points van iedere player
    # Uitleg: totaalscore van alle tegenstanders per speler toevoegen aan player
    for one_round in rounds[1:]:
        for game in one_round:
            white_player, black_player, result_white_player, result_black_player = game.split()
            players[white_player].resistance_points  = players[black_player].points
            players[black_player].resistance_points  = players[white_player].points
    # bepalen van sonneborn_berger van iedere player
    # Uitleg: 1 * punten van verliezers   0,5 * punten van gelijk toevoegen aan player
    for one_round in rounds[1:]:
        for game in one_round:
            white_player, black_player, result_white_player, result_black_player = game.split()
            if float(result_white_player) == 1:
                players[white_player].sonnenborn_berger  = 1 * players[black_player].points
            if float(result_white_player) == 0.5:
                players[white_player].sonnenborn_berger  = 0.5 * players[black_player].points
            if float(result_black_player) == 1:
                players[black_player].sonnenborn_berger  = 1 * players[white_player].points
            if float(result_black_player) == 0.5:
                players[black_player].sonnenborn_berger  = 0.5 * players[white_player].points
    # sorteren: points > resistance_points > sonneborn_berger > black (van hoog naar laag)
    print_list = []
    return_list = []
    for player in players.values():
        print_list.append(str(player))
        return_list.append(player)
    print_list = sorted(print_list, key=itemgetter(1, 2, 3, 4), reverse=True)
    return_list = sorted(return_list, key=itemgetter(1, 2, 3, 4), reverse=True)
    print(print_list)
    return return_list


determine_output("""Trees
Erik
Udo
Ronald
Truus
Omar
Cornelis
Ria
Otto
Emma
Henk
Ulrich
Cor
Piet
Theo
Thea

Trees Erik 0.5 0.5
Udo Ronald 0.5 0.5
Truus Omar 1 0
Cornelis Ria 0 1
Otto Emma 0.5 0.5
Henk Ulrich 1 0
Cor Piet 1 0
Theo Thea 1 0

Ria Cor 0.5 0.5
Theo Truus 1 0
Ronald Henk 0.5 0.5
Emma Udo 0.5 0.5
Erik Otto 0.5 0.5
Ulrich Trees 1 0
Piet Thea 1 0
Omar Cornelis 0 1""")

CodePudding user response:

There are multiple problems with your code.

To resolve the error, you can use attrgetter() instead of itemgetter(). It works similarly, but is used to get attributes, not items.

You seem to confuse the two. Attributes are usually belong to objects, and are accessed by the dot-syntax: result.name. Items are usually belong to sequences (lists, tuples, etc.) and are accessed via the my_list[item_index] syntax. Objects of your class have attributes, not items.

The other problem is the sorting of the print and returns lists. The print_list sorting does not error out, even tho you used the same method for sorting them, but also won't get sorted correctly. The issue is, that the list does not contain player_result instances, but strings. This also means the itemgetter works on them, as they are sequences - but result in some strange alphabetic ordering instead of the intended. To fix it, you should sort the list before converting to str (and like that, you only need to sort the data once).

  • Related