【问题标题】:Is it possible to create a python dictionary with keys that can accept a variety of forms?是否可以创建一个带有可以接受各种形式的键的python字典?
【发布时间】:2018-06-16 01:23:16
【问题描述】:
data = (1,1,1,1,1)

dict_letters = {(1,1,1,1,1) : 'A',
                (0,1,1,0,1) : 'B',
                (1,1,1,1,1) : 'C',   
                (1,0,1,0,1) : 'D'}

def search():
            for key in dict_letters:
                if data == key:
                    print(dict_letters[key])

search()

#when running, this would result in only 'C' being printed; 'A' was never printed

之前在我的代码中,我获得了一个由 0 和 1 组成的唯一 5 项元组(data 元组)。这 5 个项目对于区分大多数字母(例如“B”和“D”)与我字典中的其他字母至关重要。但是,当有两个键具有相同的 5 个项目时,我遇到了一个问题,因此我在元组中添加了 10 个项目(其他标识数据)以帮助进一步区分键;这是我当前字典的摘录:

data = (0,1,1,0,1,1,1,1,1,1,1,1,1,1,1)
data = (0,1,1,0,1,0,0,0,0,0,0,0,0,0,0)
data = (0,1,1,0,1,0,0,1,1,1,0,0,1,0,1)

#x = 0 or 1
dict_letters = {(1,1,1,1,1,x,x,1,x,x,x,x,x,x,x) : 'A',
                (0,1,1,0,1,x,x,x,x,x,x,x,x,x,x) : 'B',
                (1,1,1,1,1,x,x,0,x,x,x,x,x,x,x) : 'C',    
                (1,1,0,0,1,x,x,x,x,x,x,x,x,x,x) : 'D'}

def search():
            for key in dict_letters:
                if data == key:
                    print(dict_letters[key])

search()

#I need to find a way for all of the data tuples to print 'B' after running the program

在这段摘录中,我只为其中一项附加项目创造了条件,以区分“A”和“C”。我想知道是否可以忽略其他 9 个附加项目是 0 还是 1,因为它们在区分这两个键时没有用(我希望忽略的项目标有 x)。我还想忽略“B”和“D”的 10 个附加项目,因为前 5 个项目足以识别。例如,我想要一种将(0,1,1,0,1,0,0,0,0,0,0,0,0,0,0)(0,1,1,0,1,1,1,1,1,1,1,1,1,1,1)(0,1,1,0,1,0,0,1,1,1,0,0,1,0,1) 等都读取为“B”的方法,而无需为“B”编码 2047 个额外的键。

我尝试在字典之前设置x = 0 or 1x = 0 and 1,但这些不起作用,因为我发现在运行程序后出于某种原因将密钥设置为(0,1,1,0,1,1,1,1,1,1,1,1,1,1,1)

注意:我正在考虑在未来至少使用一次所有附加项目,因此删除 10 个附加项目中的任何一个都不是一种选择。

我对 Python 也比较陌生,所以如果您能尽可能简单地回答,我将不胜感激。提前谢谢!

【问题讨论】:

  • 我可以创建一个允许多个不同键输出单个值的字典条目吗?请参阅“例如,我想要一种方式……”。
  • 这听起来像XY problem。请给我们更多的上下文,以便我们可以提出比带有疯狂元组键的 dict 更好的解决方案。
  • 看起来你的元组键只是整数的二进制表示。为什么不使用它?
  • 您的编辑告诉我们更多关于您的解决方案 Y 的信息,但我们需要了解 X 问题,以便我们可以提供 Y 的替代方案。
  • 听起来您正在字典键中寻找某种通配符。是这样吗?

标签: python python-2.7 dictionary key


【解决方案1】:

我们可以为这个任务使用一个普通的dict,我们只需要为每个字母构建所有可能的键。您的键元组包含 15 个项目,每个项目有 2 个不同的值,因此最多只有 2**15 = 32768 个不同的模式,这在现代机器上相当小。

我们可以使用itertools.product 高效地生成所有模式。 product 有效地从您传递的参数创建嵌套的 for 循环。这是该技术的简短说明。以下代码生成10XX01对应的所有模式。

from itertools import product

for t in product(*[(1,), (0,), (0, 1), (0, 1), (0,), (1,)]):
    print(t)

输出

(1, 0, 0, 0, 0, 1)
(1, 0, 0, 1, 0, 1)
(1, 0, 1, 0, 0, 1)
(1, 0, 1, 1, 0, 1)

这里有一些代码使用问题中给出的数据来构建dict,您可以将其用于搜索。我们使用dict.get 方法,因此如果您查找不在字典中的模式,代码将返回None

from __future__ import print_function
from itertools import product

#x = 0 or 1
X = 'x'
letter_patterns = {
    (1, 1, 1, 1, 1, X, X, 1, X, X, X, X, X, X, X): 'A',
    (0, 1, 1, 0, 1, X, X, X, X, X, X, X, X, X, X): 'B',
    (1, 1, 1, 1, 1, X, X, 0, X, X, X, X, X, X, X): 'C',
    (1, 1, 0, 0, 1, X, X, X, X, X, X, X, X, X, X): 'D',
}

def make_dict(letter_patterns):
    ''' Build a dict of all the bit patterns for each letter '''
    xlate = {0: (0,), 1: (1,), X: (0, 1)}
    letter_dict = {}
    # Generate all of the (0, 1) combinations for each X in each pattern
    for pattern, letter in letter_patterns.items():
        for key in product(*[xlate[u] for u in pattern]):
            letter_dict[key] = letter
    return letter_dict

# test

letter_dict = make_dict(letter_patterns)

test_items = [
    ((1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0), 'A'),
    ((1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1), 'A'),
    ((1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0), 'A'),
    ((0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), 'B'),
    ((0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 'B'),
    ((0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1), 'B'),
    ((1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 'C'),
    ((1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1), 'C'),
    ((1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0), 'C'),
    ((1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 'D'),
    ((1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1), 'D'),
    ((1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), 'D'),
    ((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 'Z'),
]

# Check that each test key gets the correct letter, or returns
# None if the key isn't in letter_dict
for key, true_letter in test_items:
    letter = letter_dict.get(key)
    print(key, true_letter, letter, letter == true_letter)

输出

(1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0) A A True
(1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1) A A True
(1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0) A A True
(0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) B B True
(0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) B B True
(0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1) B B True
(1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) C C True
(1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1) C C True
(1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0) C C True
(1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) D D True
(1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1) D D True
(1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) D D True
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) Z None False

此代码在 Python 2 和 3 上都能正常运行(在 2.6.6 和 3.6.0 上测试)。您可以通过更改

使其在 Python 2 上更高效
for pattern, letter in letter_patterns.items():

for pattern, letter in letter_patterns.iteritems():


letter_patterns 中的元组不是很方便,尤其是如果你想在letter_patterns 中放入很多符号。为了减少打字,我们可以使用字符串,而不是使用那些元组。这是执行此操作的上述代码的变体。生成的letter_dict 仍然使用元组键,因为我假设这就是你从 Leap Motion 设备硬件中得到的。
letter_patterns = {
    'A': '11111xx1xxxxxxx',
    'B': '01101xxxxxxxxxx',
    'C': '11111xx0xxxxxxx',
    'D': '11001xxxxxxxxxx',
}

def make_dict(letter_patterns):
    ''' Build a dict of all the bit patterns for each letter '''
    xlate = {'0': (0,), '1': (1,), 'x': (0, 1)}
    letter_dict = {}
    # Generate all of the (0, 1) combinations for each X in each pattern
    for letter, pattern in letter_patterns.items():
        for key in product(*[xlate[u] for u in pattern]):
            letter_dict[key] = letter
    return letter_dict

【讨论】:

  • 由于某种原因,当我尝试运行它时,它给了我以下错误:File "Project.py", line 210, in make_dict \\ letter_dict[key] = letter \\ NameError: free variable 'letter_dict' referenced before assignment in enclosing scope。除此之外,我认为您实际上已经完全解决了我的问题!
  • @Dan 我想你可能犯了复制和粘贴错误。 letter_dict = {} 行创建了对letter_dict 的赋值,因此letter_dict[key] = letter 是对letter_dict 的有效引用。在将我的代码添加到您现有的项目之前,请先尝试完全运行我在这里所拥有的。
  • @Dan 确保缩进正确。我怀疑你不小心将我的函数粘贴到另一个函数中。
  • 两个版本都有效,是的,当我将代码集成到我的项目中时,我发现我错误地缩进了您的代码。不过,我很高兴地说,该代码在我的项目中完美运行!我要感谢您引导我完成这一切并在最后 2 小时内帮助我。我真的很感激你为我所做的一切。
  • @Dan 很高兴!这是一个有趣的难题,我祝你在开发你的软件时好运。当然,如果您再次遇到困难,您可以随时提出新问题。
【解决方案2】:

我的理解是,给定一些 tuple 作为键,如果此 tuple 中的某些条目与现有键不完全匹配,您希望忽略它们。

您可以通过在collections.UserDict 和自定义__getitem__ 方法的帮助下实现自己的类字典类来做到这一点。

以下实现假定tuple 中的条目是10。如果没有这个假设,它将不得不遍历所有键。

from UserDict import UserDict
# for Python 3 use this import instead:
# from collections import UserDict

from itertools import product

class WildcardDict(UserDict):
    def __getitem__(self, args):
        item, *wildcards = args

        try:
            return self.data[item]
        except KeyError:
            for xs in product((0, 1), repeat=len(wildcards)):
                xs = iter(xs)
                item = tuple(next(xs) if i in wildcards else x for i, x in enumerate(item))
                if item in self.data:
                    return self.data[item]
        raise KeyError(args)

d = WildcardDict()

d[0, 1, 1, 0, 1] = 'B'

print(d[(0, 1, 1, 0, 1), ]) # 'B'
print(d[(0, 1, 0, 0, 0), 2, 4]) # 'B'

请注意,dict 项目查找通常是 O(1),尽管这使得它 O(2k) 其中 k 是通配符的数量。特别是,这意味着如果通配符的数量不断增加,您最好使用list,其中查找将是O(n)

【讨论】:

  • 我会使用“key”而不是“item”,这与 dict.items 产生 (key, value) 对的事实保持一致。
  • @PM2Ring 我一直使用 item,PyCharm 会自动用 'item' 完成签名。所以我不想离它太远,因为有些人可能已经习惯了这种命名法。
  • @OlivierMelançon 我真的很喜欢你的方法,但我仍然不确定它是否能解决我所有的问题。 (不幸的是,由于某种原因,我无法安装“集合”,所以我只是在这里试探性地询问潜在的问题)。假设我的 data 元组是 (0,1,0,1,0) 并且字典条目显示为 (0,1,x,0,x) : 'B'。我的理解是该函数会尝试(0,1,0,1,0),这不起作用,然后(0,1,0,1)。如果这种情况继续下去,很快就会到达(0,1),这确实有效,并且会错误地打印“B”。
  • @Dan 你不必安装集合,它是一个标准库。至于第 2 和第 4 指数有什么特别之处呢?会一直被忽视吗?
  • @OlivierMelançon 感谢您的所有帮助,但我认为 PM 2Ring 的方法有效地解决了我的问题。不过,我非常感谢您为指导我完成您的方法所做的努力!
猜你喜欢
  • 1970-01-01
  • 2012-01-16
  • 2011-05-24
  • 2014-02-26
  • 1970-01-01
  • 2020-06-20
  • 1970-01-01
  • 2019-10-06
  • 2017-12-06
相关资源
最近更新 更多