Python - 带有字母数字的人类数字排序,但在 pyQt 和 __lt__ 运算符中

发布于 2024-10-21 06:52:22 字数 484 浏览 7 评论 0原文

我有数据行并希望将它们呈现如下:

1
1a
1a2
2
3
9
9.9
10
10a
11
100
100ab
ab
aB
AB

由于我使用 pyQt 并且代码包含在 TreeWidgetItem 中,所以我试图解决的代码是:

def __lt__(self, otherItem):
    column = self.treeWidget().sortColumn()

    #return self.text(column).toLower() < otherItem.text(column).toLower()

    orig = str(self.text(column).toLower()).rjust(20, "0")
    other = str(otherItem.text(column).toLower()).rjust(20, "0")
    return orig < other

I have data rows and wish to have them presented as follows:

1
1a
1a2
2
3
9
9.9
10
10a
11
100
100ab
ab
aB
AB

As I am using pyQt and code is contained within a TreeWidgetItem, the code I'm trying to solve is:

def __lt__(self, otherItem):
    column = self.treeWidget().sortColumn()

    #return self.text(column).toLower() < otherItem.text(column).toLower()

    orig = str(self.text(column).toLower()).rjust(20, "0")
    other = str(otherItem.text(column).toLower()).rjust(20, "0")
    return orig < other

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

稀香 2024-10-28 06:52:22

这可能对你有帮助。编辑正则表达式以匹配您感兴趣的数字模式。我的会将任何包含 . 的数字字段视为浮点数。使用 swapcase() 反转大小写,以便 'A' 排序在 'a' 之后。

更新:精炼:

import re

def _human_key(key):
    parts = re.split('(\d*\.\d+|\d+)', key)
    return tuple((e.swapcase() if i % 2 == 0 else float(e))
            for i, e in enumerate(parts))

nums = ['9', 'aB', '1a2', '11', 'ab', '10', '2', '100ab', 'AB', '10a',
    '1', '1a', '100', '9.9', '3']
nums.sort(key=_human_key)

print '\n'.join(nums)

输出:

1
1a
1a2
2
3
9
9.9
10
10a
11
100
100ab
ab
aB
AB

更新:(对评论的回应)如果您有一个类Foo并且想要实现__lt__ 使用 _ human_key 排序方案,只需返回 _ human_key(k1) 的结果_ human_key(k2);

class Foo(object):

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

    def __lt__(self, obj):
        return _human_key(self.key) < _human_key(obj.key)

>>> Foo('ab') < Foo('AB')
True
>>> Foo('AB') < Foo('AB')
False

因此,对于您的情况,您需要执行以下操作:

def __lt__(self, other):
    column = self.treeWidget().sortColumn()
    k1 = self.text(column)
    k2 = other.text(column)
    return _human_key(k1) < _human_key(k2)

其他比较运算符(__eq____gt__ 等)将以相同的方式实现。

This may help you. Edit the regexp to match the digit patterns you're interested in. Mine will treat any digit fields containing . as floats. Uses swapcase() to invert your case so that 'A' sorts after 'a'.

Updated: Refined:

import re

def _human_key(key):
    parts = re.split('(\d*\.\d+|\d+)', key)
    return tuple((e.swapcase() if i % 2 == 0 else float(e))
            for i, e in enumerate(parts))

nums = ['9', 'aB', '1a2', '11', 'ab', '10', '2', '100ab', 'AB', '10a',
    '1', '1a', '100', '9.9', '3']
nums.sort(key=_human_key)

print '\n'.join(nums)

Output:

1
1a
1a2
2
3
9
9.9
10
10a
11
100
100ab
ab
aB
AB

Update: (response to comment) If you have a class Foo and want to implement __lt__ using the _human_key sorting scheme, just return the result of _human_key(k1) < _human_key(k2);

class Foo(object):

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

    def __lt__(self, obj):
        return _human_key(self.key) < _human_key(obj.key)

>>> Foo('ab') < Foo('AB')
True
>>> Foo('AB') < Foo('AB')
False

So for your case, you'd do something like this:

def __lt__(self, other):
    column = self.treeWidget().sortColumn()
    k1 = self.text(column)
    k2 = other.text(column)
    return _human_key(k1) < _human_key(k2)

The other comparison operators (__eq__, __gt__, etc) would be implemented in the same way.

孤独岁月 2024-10-28 06:52:22

使用 samplebiasswapcase 想法,以及 Ned Batchelder 的 人类排序代码,你可以这样做:

import re
def human_keys(astr):
    '''
    alist.sort(key=human_keys) sorts in human order
    '''
    keys=[]
    for elt in re.split('(\d+)', astr):
        elt=elt.swapcase()
        try: elt=int(elt)
        except ValueError: pass
        keys.append(elt)
    return keys

x='''
    1
    1a
    1a2
    2
    3
    9
    9.9
    9.10
    9a2
    10
    10a
    11
    100
    100ab
    ab
    aB
    AB
    '''.split()

print(x)
assert x == sorted(x,key=human_keys)

你可以在__lt__中应用human_keys,如下所示:

def __lt__(self, otherItem):
    column = self.treeWidget().sortColumn()
    orig = str(self.text(column).toLower()).rjust(20, "0")
    other = str(otherItem.text(column).toLower()).rjust(20, "0")
    return human_keys(orig) < human_keys(other)

Using samplebias's swapcase idea, and Ned Batchelder's human-sort code, you might do it this way:

import re
def human_keys(astr):
    '''
    alist.sort(key=human_keys) sorts in human order
    '''
    keys=[]
    for elt in re.split('(\d+)', astr):
        elt=elt.swapcase()
        try: elt=int(elt)
        except ValueError: pass
        keys.append(elt)
    return keys

x='''
    1
    1a
    1a2
    2
    3
    9
    9.9
    9.10
    9a2
    10
    10a
    11
    100
    100ab
    ab
    aB
    AB
    '''.split()

print(x)
assert x == sorted(x,key=human_keys)

You could apply human_keys in __lt__ like this:

def __lt__(self, otherItem):
    column = self.treeWidget().sortColumn()
    orig = str(self.text(column).toLower()).rjust(20, "0")
    other = str(otherItem.text(column).toLower()).rjust(20, "0")
    return human_keys(orig) < human_keys(other)
汐鸠 2024-10-28 06:52:22

我不明白你的排序算法,所以我无法告诉你如何实现它。但有一个通用的技巧,那就是使用Python内置的sort函数中的key参数。换句话说,您想要对数据进行一些转换,Python 会按正确的顺序进行排序,然后将该转换编写为 Python 函数 foo 并调用 sort(data,键=foo)


示例:如果您有一个字符串列表 "-",请说 ["1-1","1-2 ","3-1"] 并且您想按第二个数字排序,然后按第一个数字排序,请注意,如果数据采用 [(1,1), (2,1), (1,3)] 即反转元组列表。因此,您可以编写一个函数

def key(s):
    l, r = s.split("-")
    return int(r), int(l)

,然后使用 sort(l, key=key) 对列表进行排序。

I don't understand your sort algorithm, so I can't tell you how to implement it. But there is a general technique, which is to use the key parameter in Python's builtin sort function. In other words, you want to come up with some transformation of your data which Python would sort in the correct order, and then write that transformation as a Python function foo and call sort(data, key=foo).


Example: if you had a list of strings "<integer>-<integer>", say ["1-1","1-2","3-1"] and you wanted to sort by the second number and then the first, notice that Python would sort the data correctly if it were in the form [(1,1), (2,1), (1,3)] i.e. a list of reversed tuples. So you would write a function

def key(s):
    l, r = s.split("-")
    return int(r), int(l)

and then sort the list with sort(l, key=key).

嗳卜坏 2024-10-28 06:52:22

这是一个函数,给定一个混合有字母和数字部分的字符串,返回一个以“自然”方式排序的元组。

def naturalkey(key, convert=int):
    if not key:
        return ()
    keys = []
    start = 0
    extra = ""
    in_num = key[0].isdigit()
    for i, char in enumerate(key):
        if start < i:
            if in_num:
                try:
                    last_num = convert(key[start:i])
                except:
                    in_num = False
                    if i > 2 and key[i-2] == ".":
                        extra = "."
                    keys.append(last_num)
                    start = i-1
            if not in_num:  # this is NOT equivalent to `else`!
                if char.isdigit():
                    keys.append(extra + key[start:i])
                    in_num = True
                    start = i
                    extra = ""
                    last_num = convert(char)
    keys.append(last_num if in_num else (extra + key[start:]))
    return tuple(keys)

它使用的基本方法是,当它看到一个数字时,它会收集额外的字符并不断尝试将结果转换为数字,直到它不能再转换为止(即它收到异常)。默认情况下,它会尝试将连续字符转换为整数,但您可以传入 convert=float 以使其接受小数点。 (不幸的是,它不接受科学记数法,因为要得到像“1e3”这样的东西,它首先会尝试解析无效的“1e”。这与+或-号一起,可能是特殊情况,但它并不看起来这对于您的用例来说不是必需的。)

该函数返回一个包含字符串和数字的元组,按照它们在字符串中找到的顺序排列,并将数字解析为指定的数字类型。例如:

naturalkey("foobar2000.exe")
>>> ("foobar", 2000, ".exe")

该元组可以用作对字符串列表进行排序的键:

my_list.sort(key=lambda i: naturalkey(i, float))

或者您可以使用它来实现比较功能:

def __lt__(self, other):
    return naturalkey(self.value, float) < naturalkey(other.value, float)

在对象的 __init__() 中生成自然键会更好(更快) 方法,将其存储在实例中,然后编写比较函数以使用存储的值。如果派生键的值是可变的,您可以编写一个属性,在更新基础值时更新键。

Here's a function that, given a string with a mixture of alphabetical and numeric parts, returns a tuple that will sort in a "natural" way.

def naturalkey(key, convert=int):
    if not key:
        return ()
    keys = []
    start = 0
    extra = ""
    in_num = key[0].isdigit()
    for i, char in enumerate(key):
        if start < i:
            if in_num:
                try:
                    last_num = convert(key[start:i])
                except:
                    in_num = False
                    if i > 2 and key[i-2] == ".":
                        extra = "."
                    keys.append(last_num)
                    start = i-1
            if not in_num:  # this is NOT equivalent to `else`!
                if char.isdigit():
                    keys.append(extra + key[start:i])
                    in_num = True
                    start = i
                    extra = ""
                    last_num = convert(char)
    keys.append(last_num if in_num else (extra + key[start:]))
    return tuple(keys)

The basic approach it uses is, when it sees a digit, it gathers additional characters and keeps trying to convert the result to a number until it can't anymore (i.e. it gets an exception). By default it tries to convert runs of characters to an integer, but you can pass in convert=float to have it accept decimal points. (It won't accept scientific notation, unfortunately, since to get something like '1e3' it would first try to parse '1e' which is invalid. This, along with the + or - sign, could be special-cased but it doesn't look like that is necessary for your use case.)

The function returns a tuple containing strings and numbers in the order they were found in the string, with the numbers parsed to the specified numeric type. For example:

naturalkey("foobar2000.exe")
>>> ("foobar", 2000, ".exe")

This tuple can be used as a key for sorting a list of strings:

my_list.sort(key=lambda i: naturalkey(i, float))

Or you can use it to implement a comparison function:

def __lt__(self, other):
    return naturalkey(self.value, float) < naturalkey(other.value, float)

It would be better (faster) to generate the natural key in the object's __init__() method, store it in the instance, and write your comparison function(s) to use the stored value instead. If the value from which the key is derived is mutable, you could write a property that updates the key when the underlying value is updated.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文