如何使用相同的方法按不同的属性快速排序对象列表?

How to quick sort list of objects by different attributes using the same method?

我的class如下:

class Member:
  def __init__(self, name, zip, hire_date, birth_date):
    self.id = id
    self.name = name
    self.zip = zip
    self.hire_date = hire_date
    self.birth_date = birth_date

  def get_name(self):
    return self.name

  def get_birth_date(self):
    return self.birth_date

  def get_hire_date(self):
    return self.hire_date

  def get_zip_code(self):
    return self.zip

我需要根据邮政编码、姓名、birth_date 和 hire_date 快速排序此 class 对象的列表。我可以使用单独的排序方法来实现这些。例如,按邮政编码快速排序:

def partition(array, begin, end):
    pivot = begin
    for i in range(begin+1, end+1):
        if array[i].get_zip_code() <= array[begin].get_zip_code():
            pivot += 1
            array[i], array[pivot] = array[pivot], array[i]
    array[pivot], array[begin] = array[begin], array[pivot]
    return pivot



def quicksort(array, begin=0, end=None):
    if end is None:
        end = len(array) - 1
    def _quicksort(array, begin, end):
        if begin >= end:
            return
        pivot = partition(array, begin, end)
        _quicksort(array, begin, pivot-1)
        _quicksort(array, pivot+1, end)
    return _quicksort(array, begin, end)

这很好用。但是现在我如何使相同的方法适用于所有属性;理想情况下,通过将附加参数传递给 quicksort() 来定义要排序的属性。我也希望不直接调用对象属性,而是使用 getter 方法。

这是按邮政编码排序后的输出

Name    Zip Hire Date   Birth Date
Cosmo Kramer 111-222 2019-06-01 1979-08-12
George Costanza 131-212 2016-06-01 1980-08-12
Jerry Seinfeld 223-212 2017-06-01 1981-06-15
John Wayne 323-212 2018-06-01 1962-06-17

您可以通过在 class 中定义另一个方法来实现此目的:

class Metric:
    b_date = "birth_date"
    h_date = "hire_date"
    zip_code = "zip_code"
    name = "name"


class Member:
    def __init__(self, name, zip, hire_date, birth_date):
        self.id = id
        self.name = name
        self.zip = zip
        self.hire_date = hire_date
        self.birth_date = birth_date

    def get_name(self):
        return self.name

    def get_birth_date(self):
        return self.birth_date

    def get_hire_date(self):
        return self.hire_date

    def get_zip_code(self):
        return self.zip

    def get_metric_value(self, metric):
        if metric == Metric.b_date:
            return self.get_birth_date()
        elif metric == Metric.h_date:
            return self.get_hire_date()
        elif metric == Metric.zip_code:
            return self.get_zip_code()
        elif metric == Metric.name:
            return self.get_name()

def partition(array, begin, end, metric):
    pivot = begin
    for i in range(begin+1, end+1):
        if array[i].get_metric_value(metric) <= array[begin].get_metric_value(metric):
            pivot += 1
            array[i], array[pivot] = array[pivot], array[i]
    array[pivot], array[begin] = array[begin], array[pivot]
    return pivot

def quicksort(array, metric, begin=0, end=None):
    if end is None:
        end = len(array) - 1
    def _quicksort(array, begin, end):
        if begin >= end:
            return
        pivot = partition(array, begin, end, metric)
        _quicksort(array, begin, pivot-1)
        _quicksort(array, pivot+1, end)
    return _quicksort(array, begin, end)

quicksort(array, Metric.zip_code)  # or any other metric ...

添加另一个参数,指示要排序的 key,或者只使用执行相同操作的 built-in sort() 方法:

class Member:
    def __init__(self, name, zip, hire_date, birth_date):
        self.id = id
        self.name = name
        self.zip = zip
        self.hire_date = hire_date
        self.birth_date = birth_date

    def get_name(self):
        return self.name

    def get_birth_date(self):
        return self.birth_date

    def get_hire_date(self):
        return self.hire_date

    def get_zip_code(self):
        return self.zip

    # Define how to print a Member
    def __str__(self):
        return f'{self.name:15} {self.zip} {self.hire_date} {self.birth_date}'

def partition(array, key, begin, end):
    pivot = begin
    for i in range(begin+1, end+1):
        if key(array[i]) <= key(array[begin]): # use key for comparison
            pivot += 1
            array[i], array[pivot] = array[pivot], array[i]
    array[pivot], array[begin] = array[begin], array[pivot]
    return pivot

def quicksort(array, key, begin=0, end=None):
    if end is None:
        end = len(array) - 1
    def _quicksort(array, key, begin, end):
        if begin >= end:
            return
        pivot = partition(array, key, begin, end)
        _quicksort(array, key, begin, pivot-1)
        _quicksort(array, key, pivot+1, end)
    return _quicksort(array, key, begin, end)

def display(L):
    print('Name            Zip     Hire Date  Birth Date')
    for item in L:
        print(item)
    print()

L =[Member('Cosmo Kramer','111-222','2019-06-01','1979-08-12'),
    Member('George Costanza','131-212','2016-06-01','1980-08-12'),
    Member('Jerry Seinfeld','223-212','2017-06-01','1981-06-15'),
    Member('John Wayne','323-212','2018-06-01','1962-06-17')]

quicksort(L,Member.get_hire_date)
display(L)
quicksort(L,Member.get_birth_date)
display(L)
# Using built-in sort()
L.sort(key=Member.get_name)
display(L)

输出:

Name            Zip     Hire Date  Birth Date
George Costanza 131-212 2016-06-01 1980-08-12
Jerry Seinfeld  223-212 2017-06-01 1981-06-15
John Wayne      323-212 2018-06-01 1962-06-17
Cosmo Kramer    111-222 2019-06-01 1979-08-12

Name            Zip     Hire Date  Birth Date
John Wayne      323-212 2018-06-01 1962-06-17
Cosmo Kramer    111-222 2019-06-01 1979-08-12
George Costanza 131-212 2016-06-01 1980-08-12
Jerry Seinfeld  223-212 2017-06-01 1981-06-15

Name            Zip     Hire Date  Birth Date
Cosmo Kramer    111-222 2019-06-01 1979-08-12
George Costanza 131-212 2016-06-01 1980-08-12
Jerry Seinfeld  223-212 2017-06-01 1981-06-15
John Wayne      323-212 2018-06-01 1962-06-17

参考Python自带排序功能的实现:

def identity(x):
    return x

def partition(array, begin, end, key):
    pivot = begin
    for i in range(begin+1, end+1):
        if key(array[i]) <= key(array[begin]):
            pivot += 1
            array[i], array[pivot] = array[pivot], array[i]
    array[pivot], array[begin] = array[begin], array[pivot]
    return pivot

def quicksort(array, begin=0, end=None, key=identity):
    if end is None:
        end = len(array) - 1
    def _quicksort(array, begin, end):
        if begin >= end:
            return
        pivot = partition(array, begin, end, key)
        _quicksort(array, begin, pivot-1)
        _quicksort(array, pivot+1, end)
    return _quicksort(array, begin, end)

用法:

quicksort(list_of_member, key=Member.get_name)
quicksort(list_of_member, key=Member.get_zip_code)