Python - 使用字典来计算键和值

Python - using dictionary to count keys and values

我是 python 课程的学生,我们创建了一个元组列表(包含 2 个元素),我们试图以各种方式对其进行操作。此外,我们要将这些元组元素转换为字典,并使用字典重新创建操作并避免 for 循环。我坚持的任务是给定一个特定的 id(可以是字典中的键或值)函数 returns 在该字典中找到的所有其他 keys/values。

为此使用字典似乎效率不高,但那是我们在课程中的部分,并且是作业特别要求的。也没有 for 循环(如果可能的话?)。回想一下,id 可以是字典中的键或值。

example_dictionary = {'A': 'C', 'R': 'E', 'D': 'A', 'L': 'R', 'C': 'D'}

def get_interactions(example_dictionary, id):
    output = ''
    for j,k in example_dictionary.items():
        if j == id:
            output = output + k + ' '
        if k == id:
            output = output + j + ' '
    return output

这段代码工作得很好,但是它 1) 有一个 for 循环(不好)和 2) 不是很 pythonic(有点碍眼)!我怎样才能更有效地使用字典并浓缩我的台词?我在Python3,谢谢!

您可以使用列表理解,尽管有人可能会争辩说它是一种 for 循环:

example_dictionary = {'A': 'C', 'R': 'E', 'D': 'A', 'L': 'R', 'C': 'D'}   
def get_interactions(dic, id):
    output =[v for k, v in dic.items() if k == id] + [k for k,v in dic.items() if v == id]
    return output

我不知道如何避免使用 for 循环,除了制作自己的 for 循环,类似于以下内容:

i = 0
def func(tup, id) {
    if i < len(dictionary_items):        
        output = False

        if tup[0] == id or tup[1] == id:
            output = id + ' '

        i += 1

        return output
}

dictionary_items = dictionary.items()

func(dictionary_items[0], id)
func(dictionary_items[1], id)
func(dictionary_items[2], id)

等等。但是,那将是丑陋的并且非常非 pythonic。

至于使您的代码更符合 Python 风格,您可以将行 output = output + k + ' ' 更改为 output += k + ' 'output = k + ' '(您将字符串 k 和 ' ' 连接到一个空字符串,输出,这对字符串 k 和 ' ').
没有任何改变 此外,您可以检查 if j == id or k == id 而不是两个单独的 if 语句,然后说 output = id + ' ',因为如果 j 或 k 等于 id,那么 return 无论哪个 j 都没有关系k 等于 id 或者如果你 return id 本身。

您必须检查所有键和值,因此总会出现某种类型的循环。 Python 有很多方法可以在不显式使用 for.

的情况下迭代(即循环)项目

在没有 for 的情况下迭代项目的一个好方法是使用 filter, map, and reduce 内置函数以及用于创建小型匿名函数的 lambda 语法。

from itertools import chain

# Get values for matching keys and vice versa
values = map(lambda x: x[1] if x[0] == id else None, dct.items())
keys = map(lambda x: x[0] if x[1] == id else None, dct.items())

# Then you filter out the None values 
# itertools.chain allows us to conveniently do this in one line
matches = filter(lambda x: x is not None, chain(keys, values))

如果您不能使用 itertools.chain,您只需要一些额外的步骤

keys = filter(lambda x: x is not None, keys)
values = filter(lambda x: x is not None, values)
matches = keys + values

如果您需要 space 分隔的值输出:

output = ' '.join(matches)

预期结果

有一个字典和名为 wanted 的值,您想创建另一个字典作为 原始的删除了所有不具有键或值等于 wanted 值的项目。

可以用pytest测试用例的形式表达,有几个场景。

import pytest
scenarios = [
    [
        # dct
        {'A': 'C', 'R': 'E', 'D': 'A', 'L': 'R', 'C': 'D'},
        # wanted
        "A",
        # expected (result)
        {'A': 'C', 'D': 'A'},
    ],
    [
        # dct
        {'A': 'C', 'R': 'E', 'D': 'A', 'L': 'R', 'C': 'D'},
        # wanted
        "E",
        # expected (result)
        {'R': 'E'},
    ],
    [
        # dct
        {'A': 'C', 'R': 'E', 'D': 'A', 'L': 'R', 'C': 'D'},
        # wanted
        "D",
        # expected (result)
        {'D': 'A', 'C': 'D'},
    ],
    [
        # dct
        {'A': 'C', 'R': 'E', 'D': 'A', 'L': 'R', 'C': 'D'},
        # wanted
        "nothere",
        # expected (result)
        {},
    ],
    [
        # dct
        {'A': 'C', 'R': 'E', 'D': 'A', 'L': 'R', 'C': 'D'},
        # wanted
        "A",
        # expected (result)
        {'A': 'C', 'D': 'A'},
    ],
]


# replace with real implementation
def get_key_or_val_itms(dct, wanted):
    # something comes here
    return result

@pytest.mark.parametrize("scenario", scenarios)
def test_it(scenario):
    dct, wanted, expected = scenario
    assert get_key_or_val_itms(dct, wanted) == expected

除了 scenarios 之外,不要理会任何事情。它列出了几个带有输入的测试场景 字典、wanted 的值和预期结果。

解决方案的基石

dict.items() - 字典到元组列表

>>>  dct = {'A': 'C', 'R': 'E', 'D': 'A', 'L': 'R', 'C': 'D'}
>>>  dct.items()
[('A', 'C'), ('R', 'E'), ('D', 'A'), ('L', 'R'), ('C', 'D')]

测试 tuple/list

中某个值的成员资格
>>> 'A'  in ('A', 'C')
True
>>> 'A'  in ('R', 'E')
False

Lambda 函数测试,如果 wanted 出现在元组中

lambda 允许 "in place" 函数定义。它经常被用在一些地方, 其中一些函数需要引用一个函数。

首先,创建命名函数tuple_wanted

>>> wanted = "A"
>>> def tuple_wanted(tpl):
...    return wanted in tpl

并测试它(注意,wanted 现在的值为 "A"):

>>> tuple_wanted(('A', 'C'))
True
>>> tuple_wanted(('R', 'E'))
False

现在创建函数。为了使用它,我们将 lambda 的结果存储在 fun:

>>> fun = lambda tpl: wanted in tpl

可以像tuple_wanted之前一样使用:

>>> fun(('A', 'C'))
True
>>> fun(('R', 'E'))
False

后面我们会直接使用lambda的结果(见filter) 将其存储到任何变量中。

filter 删除所有未通过某些测试的列表项

filter 获取测试函数和可迭代对象(例如项目列表)以通过它进行测试。

调用 filter 的结果是 iterable 中的项目列表,它通过了测试。

在我们的例子中,我们只想传递包含 wanted 值(例如 "A")

的元组
>>> filter(tuple_wanted, dct.items())
    [('A', 'C'), ('D', 'A')]
>>> filter(fun, dct.items())
    [('A', 'C'), ('D', 'A')]
>>> filter(lambda tpl: wanted in tpl, dct.items())
    [('A', 'C'), ('D', 'A')]

将包含 2 个项目的元组列表转换为字典

>>> tpllst = [('A', 'C'), ('D', 'A')]
>>> dict(tpllst)
    {'A': 'C', 'D': 'A'}

执行工作的函数

长版

这个版本来一步步解释是怎么回事:

def get_key_or_val_itms(dct, wanted):
    # dict as [(key, val), (key2, val2), ...]
    tpldct = dct.items()
    # find tuples, where either key or val equals `wanted` value
    # first make function, which detects, the tuple we search for

    def tuple_wanted(tpl):
        return wanted in tpl
    # now use it to filter only what we search for
    restpldct = filter(tuple_wanted, tpldct)
    # finally, turn the result into dict
    return dict(restpldct)

简短版

def get_key_or_val_itms(dct, wanted):
    return dict(filter(lambda tpl: wanted in tpl, dct.items()))

结论

有效(无论是长版本还是短版本的函数):

>>> dct = {'A': 'C', 'R': 'E', 'D': 'A', 'L': 'R', 'C': 'D'}
>>> wanted = "A"
>>> get_key_or_val_itms(dct, wanted)
{'A': 'C', 'D': 'A'}

如果将函数放入测试套件的文件中,调用$ py.test -sv the_file.py将输出:

$ py.test -sv the_file.py
py.test================================ test session starts =========================
=======
platform linux2 -- Python 2.7.9, pytest-2.8.7, py-1.4.31, pluggy-0.3.1 -- /home/javl/
.virtualenvs/stack/bin/python2
cachedir: .cache
rootdir: /home/javl/sandbox/stack/dict, inifile:
collected 5 items

countdict.py::test_it[scenario0] PASSED
countdict.py::test_it[scenario1] PASSED
countdict.py::test_it[scenario2] PASSED
countdict.py::test_it[scenario3] PASSED
countdict.py::test_it[scenario4] PASSED

============================= 5 passed in 0.01 seconds ==============================

可以看出,所有场景都在通过。

解释 py.test 的工作原理超出了本答案的范围,要了解更多信息,请参阅 http://pytest.org/latest/