【问题标题】:Implementing merge sort function in python class, errors在python类中实现归并排序功能,报错
【发布时间】:2018-11-18 03:47:53
【问题描述】:

所以我定义了一个函数,如果它是由它的孤独实现的,那么它在线性数组上进行合并排序时效果很好,但是如果我把它放到一个类中它就会出错。我认为这是我不太了解课程如何运作的一个很好的例子。可能与命名空间管理有关(?)。

见下文:

def sort(array):
    print('Splitting', array)
    if len(array) > 1:
        m = len(array)//2
        left = array[:m]
        right = array[m:]

        sort(left)
        sort(right)

        i = 0
        j = 0
        k = 0

        while i < len(left) and j < len(right):
            if left[i] < right[j]:
                array[k] = left[i]
                i += 1
            else:
                array[k] = right[j]
                j += 1
            k += 1

        while i < len(left):
            array[k] = left[i]
            i += 1
            k += 1

        while j < len(right):
            array[k] = right[j]
            j += 1
            k += 1
    print('Merging', array)

arr = [1,6,5,2,10,8,7,4,3,9]
sort(arr)

产生预期的正确输出:

Splitting  [1, 6, 5, 2, 10, 8, 7, 4, 3, 9]
Splitting  [1, 6, 5, 2, 10]
Splitting  [1, 6]
Splitting  [1]
Merging  [1]
Splitting  [6]
Merging  [6]
Merging  [1, 6]
Splitting  [5, 2, 10]
Splitting  [5]
Merging  [5]
Splitting  [2, 10]
Splitting  [2]
Merging  [2]
Splitting  [10]
Merging  [10]
Merging  [2, 10]
Merging  [2, 5, 10]
Merging  [1, 2, 5, 6, 10]
Splitting  [8, 7, 4, 3, 9]
Splitting  [8, 7]
Splitting  [8]
Merging  [8]
Splitting  [7]
Merging  [7]
Merging  [7, 8]
Splitting  [4, 3, 9]
Splitting  [4]
Merging  [4]
Splitting  [3, 9]
Splitting  [3]
Merging  [3]
Splitting  [9]
Merging  [9]
Merging  [3, 9]
Merging  [3, 4, 9]
Merging  [3, 4, 7, 8, 9]
Merging  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

但是,当我尝试在类中使用此函数时出现错误;我认为与命名空间管理有关。见下文:

class MergeSort(object):

    def __init__(self, array):
        self.array = array

    def sort(self):
        print('Splitting', self.array)
        if len(self.array) > 1:
            m = len(self.array)//2
            left = self.array[:m]
            right = self.array[m:]

            sort(left)
            sort(right)

            i = 0
            j = 0
            k = 0

            while i < len(left) and j < len(right):
                if left[i] < right[j]:
                    self.array[k] = left[i]
                    i += 1
                else:
                    self.array[k] = right[j]
                    j += 1
                k += 1

            while i < len(left):
                self.array[k] = left[i]
                i += 1
                k += 1

            while j < len(right):
                self.array[k] = right[j]
                j += 1
                k += 1
        print('Merging', self.array)

x = MergeSort([1,6,5,2,10,8,7,4,3,9])
x.sort()

产生错误输出:

Splitting [1, 6, 5, 2, 10, 8, 7, 4, 3, 9]
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-15-89509f86277e> in <module>()
      1 x = MergeSort([1,6,5,2,10,8,7,4,3,9])
----> 2 x.sort()

<ipython-input-14-2bba116f00ce> in sort(self)
     11             right = self.array[m:]
     12 
---> 13             sort(left)
     14             sort(right)
     15 

NameError: name 'sort' is not defined

在谷歌搜索之后,我最初的直觉是通过添加前缀 self. 来更改子例程 sort(left) 和 sort(right),但这会产生位置参数错误。希望对我在这里不理解的内容发表一两条评论。如果我的问题不愚蠢,请为好票欢呼,如果是,则为反对票。

【问题讨论】:

  • 您对排序的递归调用应该是self.sort。所以,不应该是sort(left)sort(right),而是self.sort(left)self.sort(right)
  • @TobiasBrösamle 不,这行不通。 sort 不接受参数(除了self)。而且,正如 OP 在问题中所说,他们已经尝试过“添加前缀自我”。
  • @TobiasBrösamle 如果我这样做,我会得到位置参数错误。
  • 顺便说一下,描述你写的东西而不是显示代码,然后描述错误而不是显示实际的异常,往往会导致人们误解你的问题,除非他们非常仔细地阅读它(如在第一条评论和此处的一个答案中看到),并且如果您犯了一个对其他人来说可能很明显但对您来说并不明显的简单错误,那么人们也无法对其进行调试。

标签: python algorithm class namespaces mergesort


【解决方案1】:

sort(left) 不起作用的原因是,正如您推测的那样,如果不指定 self,就无法在 self 上调用方法。关闭它意味着它会查找本地或全局名称 sort,找不到,并引发 NameError

self.sort(left) 不起作用的原因是您定义的 API 不起作用。您的类将列表作为构造函数参数,然后采用不带参数的sort,它在构造时对传入的列表进行操作。因此,您无法使用不同的数组调用您自己的sort。如果您尝试self.sort(left),则您传递的参数数量错误,就像调用abs(1, 2) 一样,您会得到相同的TypeError

您必须按照设计的方式使用 API:使用新列表创建一个新的 MergeSort 排序器对象,然后在该新对象上调用 sort

leftsorter = MergeSort(left)
leftsorter.sort()
rightsorter = MergeSort(right)
rightsorter.sort()

【讨论】:

  • @data83 这对于类的使用可能与您为此函数所能想到的一样好。只是合并排序中除了实际排序之外确实没有任何行为,并且除了列表本身之外没有任何持久状态,因此它不是变成类的最佳示例。如果您可以找到一个函数(可能在重构为一堆小函数之后)具有需要在函数调用之间持续存在的状态,那么这可能是一个更好的候选者。但无论如何,尝试一切都没有坏处,即使有些事情没有成功……
  • @EvgenyPogrebnyak 他的设计修改了要排序的列表。这是一件完全合理的事情。如果这样做,他们不应该返回排序列表。
  • 通过在类中 sort( ) 函数的最后一行添加return(self.array) 来解决。
  • @data83 首先,这不是问题,所以不应该解决。而且在递归函数中尤其容易产生误导,因为递归调用与返回值没有任何关系。
  • @data83 我的意思是,我认为您根本不想在此处返回列表。调用者仍然可以访问x.array。通常的 Python 习惯用法是,如果函数的主要目的是改变你的自身或参数,你会返回 None,正如官方常见问题解答中的某处所解释的那样,但我不再在我的电脑前了。
【解决方案2】:

将我的类中 sort() 函数的 sort(left)sort(right) 组件替换为

leftsorter = MergeSort(left)
leftsorter.sort()
rightsorter = MergeSort(right)
rightsorter.sort()

(谢谢你)

同时从代码中删除调试打印语句(感谢 Evgany P),并通过避免重用内置函数名称 sort() 以避免混淆,我有一个工作 MergeSort 类。

class MergeSort(object):

    def __init__(self, array):
        self.array = array

    def merge_sort(self):
        if len(self.array) > 1:
            m = len(self.array)//2
            left = self.array[:m]
            right = self.array[m:]

            leftsorter = MergeSort(left)
            leftsorter.merge_sort()
            rightsorter = MergeSort(right)
            rightsorter.merge_sort()

            i = 0
            j = 0
            k = 0

            while i < len(left) and j < len(right):
                if left[i] < right[j]:
                    self.array[k] = left[i]
                    i += 1
                else:
                    self.array[k] = right[j]
                    j += 1
                k += 1

            while i < len(left):
                self.array[k] = left[i]
                i += 1
                k += 1

            while j < len(right):
                self.array[k] = right[j]
                j += 1
                k += 1

x = MergeSort([3,5,6,2,1,4,10,9,8,7])
x.merge_sort()
x.array

输出[ ]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

大家干得好!

【讨论】:

    【解决方案3】:

    您需要在班级内致电self.sort()

    一个更大的问题是你的函数或类方法都没有返回任何东西,只是打印,你对此满意吗?

    【讨论】:

    • 首先,self.sort 不起作用。其次,如果该方法就地改变列表,它不必(实际上也不应该)返回任何东西。
    • @abarnet,该方法可以在实例变量上工作,但为什么该函数只是打印? print(sort(x)) 更加明确
    • 调用self.sort() 只会重新开始排序你正在排序的同一个列表,这只会导致无限递归。它不会对左半部分进行排序,这是 OP 正在尝试做的。我同意print 不是一个伟大的设计,但它对于调试目的很有用,所以也许这就是他这样做的原因?但是print(sort(x)) 也不好。在 Python 中,就地改变对象的函数通常不会返回这些对象。就像 list.sort() 不返回排序后的 self 一样,MergeSort.sort() 不返回 its 排序后的 self.array 是惯用的。
    • @abarnert,即使出于调试目的,如果未返回排序后的数组,该函数也毫无意义。我不明白你所说的“就地改变对象的功能”是什么意思,你能举个例子吗?这可能是一个类方法,但对于独立的排序函数来说毫无意义。当添加一个返回值时,原始排序给出了错误的递归,这可能是 OP 寻找一个类的原因,但是是错误的。顺便说一句, print(sort(x)) 是这个函数的正确方向,考虑到你不会调用 list.sort() 一个函数,而是一个方法。
    • 我不知道如何解释“就地突变”。也许尝试运行 OP(工作,非类版本)代码,并在 sort(arr) 之后执行 print(arr),你会很清楚。
    猜你喜欢
    • 2021-04-25
    • 1970-01-01
    • 2012-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-17
    • 2014-04-21
    相关资源
    最近更新 更多