【问题标题】:Same Python code returns different results for same input dictionary相同的 Python 代码为相同的输入字典返回不同的结果
【发布时间】:2016-08-02 17:00:15
【问题描述】:

我有一本名为 dico 的字典,其中包含以下信息:

('libc6-i386', '1.06')  :  lib32tinfo5
('lib32c-dev', '')  :  lib32tinfo-dev
('libc6-x32', '2.16')  :  libx32tinfo5
('libc6-i386', '2.4')  :  lib32ncursesw5
('libc-dev', '')  :  libncursesw5-dev
('libncurses5-dev', '5.9+20150516-2ubuntu1')  :  libncurses5-dbg
('libc6-dev', '')  :  libncursesw5-dev
('libc6-dev-x32', '')  :  libx32tinfo-dev
('libc6-i386', '2.16')  :  lib32tinfo5
('libncursesw5-dev', '5.9+20150516-2ubuntu1')  :  libncursesw5-dbg

当我调用 comparePackages(dico) 时,我不断得到相同输入的不同结果,例如:

('lib32c-dev', 'Not Specified') lib32tinfo-dev
('libc6-dev', 'Not Specified') libncursesw5-dev
('libc-dev', 'Not Specified') libncursesw5-dev
('libc6-i386', '2.4') lib32ncursesw5
('libc6-x32', '2.16') libx32tinfo5
('libncurses5-dev', '5.9+20150516-2ubuntu1') libncurses5-dbg
('libc6-dev-x32', 'Not Specified') libx32tinfo-dev
('libc6-i386', '2.16') lib32tinfo5
('libncursesw5-dev', '5.9+20150516-2ubuntu1') libncursesw5-dbg

('libncurses5-dev', '5.9+20150516-2ubuntu1') libncurses5-dbg
('libncursesw5-dev', '5.9+20150516-2ubuntu1') libncursesw5-dbg
('libc6-i386', '2.16') lib32tinfo5
('lib32c-dev', 'Not Specified') lib32tinfo-dev
('libc6-x32', '2.16') libx32tinfo5
('libc6-dev', 'Not Specified') libncursesw5-dev
('libc6-i386', '2.4') lib32ncursesw5
('libc-dev', 'Not Specified') libncursesw5-dev

('libc6-i386', '2.4') lib32ncursesw5
('libc-dev', 'Not Specified') libncursesw5-dev
('libc6-i386', '1.06') lib32tinfo5
('lib32c-dev', 'Not Specified') lib32tinfo-dev
('libncurses5-dev', '5.9+20150516-2ubuntu1') libncurses5-dbg
('libncursesw5-dev', '5.9+20150516-2ubuntu1') libncursesw5-dbg
('libc6-i386', '2.16') lib32tinfo5
('libc6-dev', 'Not Specified') libncursesw5-dev
('libc6-x32', '2.16') libx32tinfo5

这是我的 comparePackages 函数

import apt_pkg
apt_pkg.init_system()

"""
    Function that compares package versions and keeps the most recent one.
    @param : dictionary
        package_dictionary :
            keys : tuple
            values : string
"""

def comparePackages(package_dictionary):
    if(type(package_dictionary) is dict):
        list_keys = list(package_dictionary.keys()) #list of tuples
        list_values = list(package_dictionary.values()) #list of strings
        #list_keys, list_values = list(package_dictionary.items())
        dic = {}

        for x,y in zip(range(0,len(list_keys)-1),range(0,len(list_values)-1)):
            #if 2 tuples have the same package name
            if(list_keys[x][0] == list_keys[x+1][0]):
                print("Start comparing")

                #second element of the tuple x
                a = str(list_keys[x][1])

                #second element of the tuple x+1
                b = str(list_keys[x+1][1])

                #compare versions
                vc = apt_pkg.version_compare(a,b)

                if vc > 0:
                    #a>b
                    print("a>b")
                    max_version = a

                elif vc == 0:
                    #a==b
                    print("a==b")
                    max_version = a 

                elif vc < 0:
                    #a<b
                    print("a<b")
                    max_version = b

                if(max_version is '') :
                    max_version = 'Not Specified'
                #create a dict that for each package name has the most recent version
                dic[(list_keys[x][0],max_version)] = list_values[y]

            else:
                version = list_keys[x][1]
                if(version is '') :
                    version = 'Not Specified'
                dic[(list_keys[x][0],version)] = list_values[y]

        for k,v in dic.items():
            print(k,v)

    else:
        raise TypeError("Argument of comparePackages must be a dictionary.")

预期输出:

('lib32c-dev', 'Not Specified')  :  lib32tinfo-dev
('libc6-x32', '2.16')  :  libx32tinfo5
('libc-dev', 'Not Specified')  :  libncursesw5-dev
('libncurses5-dev', '5.9+20150516-2ubuntu1')  :  libncurses5-dbg
('libc6-dev', 'Not Specified')  :  libncursesw5-dev
('libc6-dev-x32', 'Not Specified')  :  libx32tinfo-dev
('libc6-i386', '2.16')  :  lib32tinfo5 (or lib32ncursesw5)
('libncursesw5-dev', '5.9+20150516-2ubuntu1')  :  libncursesw5-dbg

我想知道为什么以及如何纠正它。最后,我需要的是一个字典,对于元组中存在的每个包都有最新版本。丢失同一个包的 2 个值中的 1 个不是问题。谢谢。

【问题讨论】:

  • 您需要向我们展示 minimal reproducible example - 符合您的数据格式的最小 dict 文字、此数据的预期结果以及您的代码当前返回的输出。
  • 字典是无序的,Python解释器的每次运行都会产生一个新的随机哈希种子来防止令人讨厌的DOS攻击(攻击者很容易使你的Python进程过载)。因此,将键与“下一个”键进行比较是行不通的。
  • @MartijnPieters - ...产生了一个新的随机哈希种子来防止令人讨厌的 DOS 攻击... 我想更好地理解这一点。可以提供一些详细解释这一点的资源链接吗?
  • @JasonEstibeiro:请参阅documentation for object.__hash__() 中的框注。
  • 我注意到我永远无法比较两个键,这就是我创建键列表和值列表的原因。我必须比较字典键中多次存在的包的版本。

标签: python python-3.x dictionary python-3.5


【解决方案1】:

这不是您问题的直接答案,但您需要解决该问题才能获得有效且可重现的行为。 (如果您的错误在此修复后消失,我不会感到震惊)

在您的代码中:

for key, value in zip(dic.keys(), dic.values())

还有:

list_keys = list(package_dictionary.keys()) #list of tuples
list_values = list(package_dictionary.values()) #list of strings

这假设键和值以相同的顺序排序。这不一定是真的,这会导致基于返回顺序的执行差异。 你应该改用items

for key, value in dict.item())

还有:

list_keys, list_values = list(package_dictionary.items())

所以:

for x,y in zip(range(0,len(list_keys)-1),range(0,len(list_values)-1))

变成:

for idx, (x, y) in enumerate(zip(list_keys, list_values)):

或者,更pythonic:

for k, v in package_dictionary.items():

另一种解决方案是使用 OrderedDict。

【讨论】:

  • 感谢您的回答。我按照你的建议做了,但是当我做 list_keys, list_values = list(package_dictionary.items()) 我得到一个 ValueError:too many values to unpack (expected 2)。
  • 键和值按相同的顺序排序,前提是没有对字典进行编辑。但是,它们在解释器运行之间的顺序不会相同。
  • @Marc:请按照此答案中的说明更新您的 for 循环。但是,此答案不会解决您的问题; 可能OrderedDict 可以帮助您,前提是您每次都以相同的顺序将条目传递到其中。
  • 其实我做了一个测试来验证条目是否有序。 x 和 y 具有相同的值。但我确实用@aluriak 建议的所有内容更新了我的代码。
  • for x,y in zip(range(0,len(list_keys)-1),range(0,len(list_values)-1)) 必须保留,因为我需要访问类型内容和将其与列表中的另一个元组进行比较
【解决方案2】:

我得到了答案

""" 比较包版本并保留最新版本的功能。 @param:字典 包字典: 键:元组 值:字符串 """

import apt_pkg
apt_pkg.init_system()

def compareVersion(x,l):
    if(type(l) is list):
        max_version = l[0]
        for e in range(1, len(l)-1):
            #vc = resultat de compare versions
            vc = apt_pkg.version_compare(l[e],l[e+1])

            if ((vc > 0) and (apt_pkg.version_compare(l[e],max_version)) > 0):  
                #a>b
                max_version = l[e]

            elif ((vc == 0) and (apt_pkg.version_compare(l[e],max_version)) > 0):
                #a==b
                max_version = l[e] 

            elif ((vc < 0) and (apt_pkg.version_compare(max_version,l[e+1])) < 0):
                #a<b
                max_version = l[e+1]

        if(max_version is '') :
            max_version = 'Not Specified'
                #create a dict that for each package name has the most recent version
        #print(x,l,max_version)
        return max_version
    else:
        raise TypeError("Argument must be a list.")


def comparePackages(package_dictionary):
    if(type(package_dictionary) is dict):
        list_keys = list(package_dictionary.keys()) #list of tuples
        list_values = list(package_dictionary.values()) #list of strings

        dic = {}

        for x in list_keys: #for each element in the list
            l = []
            for h in list_keys:
                if x[0] == h[0]: #if nom package x == nom package h
                    a = str(x[1]) #a = version du package x
                    b = str(h[1]) #b = version du package y
                    if(l.count(a) == 0):
                        l.append(a)
                    if(l.count(b) == 0):
                        l.append(b)
            max_version = compareVersion(x,l)   
            dic[(x[0],max_version)] = list_values[list_keys.index(x)]

        return dic    
    else:
        raise TypeError("Argument of comparePackages must be a dictionary.")    

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多