2010-07-07 4 views
2

Мне не удается получить этот код для сортировки списка объектов с помощью .sort() или sorted(). Что мне здесь не хватает?Нужна помощь в сортировке списка объектов по ключу

P.S. Мой метод solution.distance() может использовать некоторые косметические операции, если у кого есть какие-либо предложения.

Спасибо!

import random 
import math 

POPULATION_SIZE = 100 

data = [[1, 565.0, 575.0], 
     [2, 25.0, 185.0], 
     [3, 345.0, 750.0], 
     [4, 945.0, 685.0], 
     [5, 845.0, 655.0], 
     [6, 880.0, 660.0], 
     [7, 25.0, 230.0], 
     [8, 525.0, 1000.0], 
     [9, 580.0, 1175.0], 
     [10, 650.0, 1130.0] 
     ] 

class Solution(): 

    def __init__(self): 
    self.dna = [] 
    self.randomize() 

    def randomize(self): 
    temp = data[:] 
    while len(temp) > 0: 
     self.dna.append(temp.pop(random.randint(0,len(temp)-1))) 

    def distance(self): 
    total = 0 
    #There has to be a better way to access two adjacent elements. 
    for i, points in enumerate(self.dna): 
     if i < (len(self.dna)-1): 
     total += math.sqrt((points[1]-self.dna[i+1][1])**2 + (points[2]-self.dna[i+1][2])**2) 
     else: 
     total += math.sqrt((points[1]-self.dna[0][1])**2 + (points[2]-self.dna[0][2])**2) 
    return int(total) 


class Population(): 

    def __init__(self): 
    self.solutions = [] 
    self.generation = 0 

    #Populate with solutions 
    self.solutions = [Solution() for i in range(POPULATION_SIZE)] 


    def __str__(self): 

    result = '' 

    #This is the part that is not returning sorted results. I tried sorted() too. 
    self.solutions.sort(key=lambda solution: solution.distance, reverse=True) 


    for solution in self.solutions: 
     result += 'ID: %s - Distance: %s\n' % (id(solution), solution.distance()) 

    return result 


if __name__ == '__main__': 

    p = Population() 
    print p 
+0

Немного больше объяснений того, что вы пытаетесь достичь, было бы полезно. – Wilduck

ответ

3

Изменить

key=lambda solution: solution.distance 

в

key=lambda solution: solution.distance() 

(. Скобки необходимы для вызова функции)

В качестве альтернативы, вы можете сделать метод distance свойство:

@property 
    def distance(self): 
     .... 

В этом случае измените все входы solution.distance() на solution.distance. Я думаю, что это альтернативное решение немного лучше, поскольку оно удаляет два символа беспорядка (parens) каждый раз, когда вы хотите поговорить о расстоянии.

PS. key=lambda solution: solution.distance возвращал связанный метод solution.distance для каждого solution в self.solutions. Поскольку тот же объект возвращался в качестве ключа для каждого solution, никакого желаемого упорядочения не произошло.

+0

Декодер @property работал для меня. Спасибо за помощь! – Alex

1

Вот попытка очистки вашего класса, с использованием функциональных методов программирования:

import random 

class Solution(): 

    def __init__(self): 
    self.dna = [] 
    self.randomize() 

    def randomize(self): 
    self.dna = data 
    random.shuffle(self.dna) 

    def distance(self): 
    # Return the distance between two points. 
    def point_distance((p1, p2)): 
     return math.sqrt((p1[1]-p2[1])**2) + (p1[2]-p2[2])**2) 
    # sums the distances between consecutive points. 
    # zip pairs consecutive points together, wrapping around at end. 
    return int(sum(map(point_distance, zip(self.dna, self.dna[1:]+self.dna[0:1]))) 

Это проверялось, но должно быть близко к работе. Кроме того, было предложено использовать класс вместо 3-элементного списка для данных. Это сделает ваш код гораздо понятнее читать:

def point_distance((p1, p2)): 
     return math.sqrt((p1.x-p2.x)**2) + (p1.y-p2.y)**2) 
+0

Вам нужна 'self.dna' в последней строке –

+0

@ David: Спасибо, просто редактировал это :) – Stephen

1

Вот лучший способ, чтобы написать цикл в distance(): положить следующее определение функции, взятые из itertools documentation, в вашем коде:

from itertools import izip, tee 
def pairwise(iterable): 
    a, b = tee(iterable) 
    next(b, None) 
    return izip(a, b) 

Тогда вы можете написать distance воспользоваться эффективными процедурами манипулирования итератора языка Python, например, так:

from itertools import chain, islice 
def distance(self): 
    all_pairs = islice(pairwise(chain(self.dna, self.dna)), 0, len(self.dna)) 
    return sum(math.sqrt((p[1]-q[1])**2 + (p[2]-q[2])**2) for p,q in all_pairs) 

Thi s должно быть достаточно эффективным, даже если массив dna очень длинный.

+0

Я думаю, что попарно не крутится вокруг массива (что, если я могу сказать из кода, это то, что OP хочет) – Stephen

+0

Вот почему я использовал цепочку, чтобы в основном объединить две копии массива. 'Islice (парная (цепочка (...)))' конструкция будет (должна) перебирать через '(a [0], a [1]), (a [1], a [2]), ... (а [N-1], а [0]) '. –

+0

Я вижу. Круто. Я должен прочитать itertools doc :) – Stephen

Смежные вопросы