How to remove an item from a list that is linked to another without losing that data in the other list?

2

Hello friends, I hope you can help me with this.

I have this object

class Player(Game):
"""docstring for Jugador"""
def __init__(self, name, sex):
    self.name = name
    self.sex = sex
    self.points = 0
    self.efectividad = 0
    self.wins = 0
    self.lose = 0
    self.rank = 0

def changeSex(self, sex):
    """ Change the Player Sex """
    self.sex = sex

def addPoints(self, points):
    """ Add points a player """
    self.points = points

def win(self):
    self.wins += 1

def lose(self):
    self.lose += 1

Which is created and stored in a list with this function

def addPlayer(self, name, sex):
    self.players.append( Player(name, sex) )

this method belongs to this class

class Game(object):
"""docstring for Game"""
def __init__(self, rounds, name, mode, numberOfTables):
    super(Game, self).__init__()
    self.roundGame = 1
    self.rounds = rounds
    self.name = name
    self.mode = mode
    self.numberOfTables = numberOfTables
    self.tables = []
    self.pointsOfTheRound = []
    self.players = []
    self.ranking = []

def changeName(self, name):
    """ Change the Player name or Game name """
    self.name = name

def addPlayer(self, name, sex):
    self.players.append( Player(name, sex) )

def addPointsToRound(self, points):
    self.pointsOfTheRound.append( points )

def nextRound(self, roundGame):

    if len(self.tables) == self.numberOfTables:
        roundGame = "Ronda " + str(self.roundGame)
        pointsOfTheRound = {roundGame : self.tables}
        self.addPointsToRound(pointsOfTheRound)

        if (self.roundGame == self.rounds):
            print("Ultima Ronda") 
        elif (self.roundGame <= self.rounds ):
            self.roundGame += 1

        self.tables = []
    else:
        print("Introduzca los puntos de todas las mesas antes de avanzar de ronda")

def getCountOfPlayers(self):
    return len(self.players)

def getRaking(self):
    print(self.ranking)

After starting the game and start assigning the points when advancing the round, I want to make a table of players in which, the players will be placed in order higher score to lower, that is why the object player has a points and effectiveness attribute, if there is a player that has the same score as another, it would be to evaluate the effectiveness that this player had against the other during the game, my problem is that when I want to assess who is the best player to go ordering the ranking from highest to lowest, I can determine the player who has more points or better effectiveness with this function

def getBestPlayers(gameClass):
players, mayor = gameClass.players, 1

for i in range(gameClass.getCountOfPlayers() - 1):

    if i >= 1:

        if players[mayor].points > players[i - 1].points:
            mayor = mayor
        elif players[mayor].points == players[i - 1].points:

            if players[mayor].efectividad > players[i - 1].efectividad:
                mayor = mayor
            elif players[mayor].efectividad == players[i - 1].efectividad:
                print("Empate 1")
            else:
                mayor = i - 1

        else:
            mayor = i - 1

return mayor

Effectively return the player with the best score or best effectiveness, but if there are 10 players, I have to perform 10 times the function to get who would be the next player with better score or effectiveness and will always return the same player, which I I thought it was, I have my list called ranking and in this insert the player that throws me the function and I will have my ranking, if they set the object Game already contains this list as an attribute and towards it the players will return the function, the thing is that for me to throw another player, I came up with the method .pop() of the lists and effectively returns me the next player with better ranking because it would not be the previous one in the list, when I check my ranking list , I have it empty and if I check the list of players, I also have it empty, since the .pop() method eliminates the objecto player from both lists and that is my problem.

I want to get the players of self.players , which is my list of players and assign them to players = gameClass.players my local list of the function getBestPlayers(gameClass) and that when I apply the method .pop() to players only be removed from this list and not from the list self.players of the class Game , how can I achieve this?

    
asked by Enrique Mora 02.06.2017 в 21:42
source

2 answers

1

If I understand you well I think you are complicating yourself unnecessarily, Python already has the ability to sort objects using their attributes by sort and operator.attrgetter simply (see documentation ). To show it I will simplify your code so that the example can be easily reproduced:

import operator

class Player():
    def __init__(self, name, points, efectividad):
        self.name = name
        self.points = points
        self.efectividad = efectividad


datos = (('Juan', 10, 5),
         ('Maria', 10, 4),
         ('Carlos', 15, 14),
         ('Laura', 15, 16),
         ('Andres', 5, 4),
         ('Lucia', 8, 3),
         ('Mario', 5, 5))

jugadores = [Player(nombre, puntos, efect) for (nombre, puntos, efect) in datos]

#Imprimimos los jugadores para comprobar su orden actual
print('\nJUGADORES SIN ORDENAR:')
for j in jugadores:
    print('{}:\n    Puntos: {}.\n    Efectividad: {}.'.format(j.name, j.points, j.efectividad))

#Ordenamos
jugadores.sort(key=operator.attrgetter('points', 'efectividad'), reverse = True)


#Volvemos a imprimir la lista para ver el resultado
print('\nJUGADORES ORDENADOS:')
for j in jugadores:
    print('{}:\n    Puntos: {}.\n    Efectividad: {}.'.format(j.name, j.points, j.efectividad))

The key and what you can use is:

import operator

jugadores.sort(key=operator.attrgetter('points', 'efectividad'), reverse = True)

This orders the objects in the list from largest to smallest, using first their attribute points and if this is equal use the attribute efectividad . If you would like an ordered copy of the list instead of ordering the list in place use sorted :

ordenados = sorted(jugadores, key=operator.attrgetter('points', 'efectividad'), reverse = True)

The output is:

JUGADORES SIN ORDENAR:
Juan:
    Puntos: 10.
    Efectividad: 5.
Maria:
    Puntos: 10.
    Efectividad: 4.
Carlos:
    Puntos: 15.
    Efectividad: 14.
Laura:
    Puntos: 15.
    Efectividad: 16.
Andres:
    Puntos: 5.
    Efectividad: 4.
Lucia:
    Puntos: 8.
    Efectividad: 3.
Mario:
    Puntos: 5.
    Efectividad: 5.

JUGADORES ORDENADOS:
Laura:
    Puntos: 15.
    Efectividad: 16.
Carlos:
    Puntos: 15.
    Efectividad: 14.
Juan:
    Puntos: 10.
    Efectividad: 5.
Maria:
    Puntos: 10.
    Efectividad: 4.
Lucia:
    Puntos: 8.
    Efectividad: 3.
Mario:
    Puntos: 5.
    Efectividad: 5.
Andres:
    Puntos: 5.
    Efectividad: 4.

Apart from this, your initial problem is that you need a dead copy of your list self.players since doing something like players = gameClass.players only does that self.players as players share the same reference (point to the same object). This makes modifying one variable modify the other since they point to the same object.

To solve these problems, use copy.deepcopy () :

import copy
players = copy.deep.copy(gameClass.players)

Now both lists are different objects and also the objects they contain are. Deleting or modifying any object in one of the lists does not alter the other. However, it is very inefficient compared to the previous one since you not only duplicate all the objects in memory, you also need several for and the use of methods like pop that make this method much more inefficient.

    
answered by 02.06.2017 / 22:28
source
0

I think I will not add much to what you said @FJSevilla, the example that I can give you is as follows:

class Player():

  def __init__(self, nombre, points, efectividad):
    self.nombre = nombre
    self.points = points
    self.efectividad = efectividad

  def __str__(self):
    return "{} ({},{})".format(self.nombre, self.points, self.efectividad)

class Game():

  def __init__(self):
    self.players = list()

  def add_player(self, nombre, points, efectividad):
    self.players.append(Player(nombre, points, efectividad))

  def get_best_players_list(self):
    self.players.sort(key=lambda x: (x.points, x.efectividad, x.nombre), reverse=True)
    return self.players

g = Game()

g.add_player("juan", 100, 90)
g.add_player("alberto", 55, 80)
g.add_player("luis", 100, 60)
g.add_player("rolando", 100, 60)
g.add_player("pedro", 210, 60)

for p in g.get_best_players_list():
   print(p)

It's a very simplified version of your code. The logic for obtaining an ordered list is found in the get_best_players_list method that uses a lambda function to define what attributes to order:

self.players.sort(key=lambda x: (x.points, x.efectividad, x.nombre), reverse=True)

the name in the order is surely over, so the only thing that would leave it is if two players maintain the same points and effectiveness and we want to not change the position in the list

The alternative of @FJSevilla to use operator.attrgetter I understand that it is better and faster. The order applies directly to the list self.players , so if you need a copy that is not a reference as you were asked in the previous answer you have to go to deepcopy . I added to the class Player a very useful method __str__ that allows to build a representation of the object, in this way a print(objeto) will show more friendly information. The final output would be something like this:

pedro (210,60)
juan (100,90)
rolando (100,60)
luis (100,60)
alberto (55,80)
    
answered by 02.06.2017 в 23:43