Метод asDict в pyparsing переопределяет предыдущий ключ - PullRequest
2 голосов
/ 08 июля 2019

У меня есть надуманный пример проблемы, с которой я сталкиваюсь:

import pyparsing as pp

fname = pp.OneOrMore( pp.Word("Max") ).setResultsName("fname")
mname = pp.OneOrMore(s pp.Word("Joseph") ).setResultsName("mname")
lname = pp.OneOrMore( pp.Word("Andrews") ).setResultsName("lname")
another_mname = pp.OneOrMore(pp.Word("Miller")).setResultsName("mname")

full = fname + mname + lname + another_mname

output = full.parseString("Max Joseph Joseph Andrews Miller Miller").asDict()

# current output 
{'fname': ['Max'], 'lname': ['Andrews'], 'mname': ['Miller', 'Miller']}

Очевидно, почему вывод такой, какой он есть.Тем не менее, я хотел бы также собрать «Джозеф» в качестве еще одной ценности.например,

# desired output
{'fname': ['Max'], 'lname': ['Andrews'], 'mname': ['Joseph', 'Joseph', 'Miller', 'Miller']}

Спасибо.

1 Ответ

2 голосов
/ 08 июля 2019

Ваш код не работает, потому что вы задаете имя результатов, которые будут одинаковыми.Это приводит к тому, что запись "mname", связанная с mname в результирующем dict(), будет заменена на запись "mname", связанную с another_mname.

Одним из способов решения этой проблемы является сбор именв два отдельных результата и затем объедините их:

import pyparsing as pp

fname = pp.OneOrMore(pp.Word("Max"))("fname")
mname = pp.OneOrMore(pp.Word("Joseph"))("mname")
lname = pp.OneOrMore(pp.Word("Andrews"))("lname")
another_mname = pp.OneOrMore(pp.Word("Miller"))("mname2")

full = fname + mname + lname + another_mname

output = full.parseString("Max Joseph Joseph Andrews Miller Miller").asDict()
print(output)
# {'fname': ['Max'], 'mname': ['Joseph', 'Joseph'], 'lname': ['Andrews'], 'mname2': ['Miller', 'Miller']}

# clean-up dict
output['mname'] = output['mname'] + output['mname2']
del output['mname2']

print(output)
# {'fname': ['Max'], 'mname': ['Joseph', 'Joseph', 'Miller', 'Miller'], 'lname': ['Andrews']}

Обратите внимание, что вы не можете просто определить mname как:

mname = pp.OneOrMore(pp.Word("Joseph") | pp.Word("Miller"))("mname")

Это может привести к аналогичной проблеме:

import pyparsing as pp

fname = pp.OneOrMore(pp.Word("Max"))("fname")
mname = pp.OneOrMore(pp.Word("Joseph") | pp.Word("Miller"))("mname")
lname = pp.OneOrMore(pp.Word("Andrews"))("lname")

full = fname + mname + lname + mname

output = full.parseString("Max Joseph Joseph Andrews Miller Miller").asDict()
print(output)
# {'fname': ['Max'], 'mname': ['Miller', 'Miller'], 'lname': ['Andrews']}

но по другой причине: теперь mname в конце full заменяет предыдущее значение mname.


Можно также автоматизироватьэто, например,

import pyparsing as pp

fname = pp.OneOrMore(pp.Word("Max"))("fname")
mname = pp.OneOrMore(pp.Word("Joseph"))("mname:0")
lname = pp.OneOrMore(pp.Word("Andrews"))("lname")
another_mname = pp.OneOrMore(pp.Word("Miller"))("mname:1")

full = fname + mname + lname + another_mname

output = full.parseString("Max Max Joseph Joseph Andrews Miller Miller").asDict()
print(output)
# {'fname': ['Max', 'Max'], 'mname:0': ['Joseph', 'Joseph'], 'lname': ['Andrews'], 'mname:1': ['Miller', 'Miller']}


def quench(pp_dict, mapping=lambda k: k.split(':')[0]):
    result = {}
    to_remove = []
    for k, v in pp_dict.items():
        new_k = mapping(k)
        if k != new_k:
            if new_k not in result:
                result[new_k] = []
            result[new_k].extend(v)
        else:
            result[k] = v
    return result


print(quench(output))
# {'fname': ['Max', 'Max'], 'mname': ['Joseph', 'Joseph', 'Miller', 'Miller'], 'lname': ['Andrews']}

или, что еще более бессмысленно, путем предварительной обработки full путем автоматического преобразования нескольких "mname" экземпляров в пронумерованных (например, "mname:0") для последующего гашения.


РЕДАКТИРОВАТЬ

(как указано @PaulMcG)

Этот механизм реализован непосредственно в pyparsing:

import pyparsing as pp

fname = pp.OneOrMore(pp.Word("Max")).setResultsName("fname")
mname = pp.OneOrMore(pp.Word("Joseph")).setResultsName("mname", listAllMatches=True)
lname = pp.OneOrMore(pp.Word("Andrews")).setResultsName("lname")
another_mname = pp.OneOrMore(pp.Word("Miller")).setResultsName("mname", listAllMatches=True)

full = fname + mname + lname + another_mname

output = full.parseString("Max Joseph Joseph Andrews Miller Miller").asDict()
print(output)
# {'fname': ['Max'], 'mname': [['Joseph', 'Joseph'], ['Miller', 'Miller']], 'lname': ['Andrews']}

илидаже так:

import pyparsing as pp

fname = pp.OneOrMore(pp.Word("Max")).setResultsName("fname")
mname = pp.OneOrMore(pp.Word("Joseph") | pp.Word("Miller")).setResultsName("mname", listAllMatches=True)
lname = pp.OneOrMore(pp.Word("Andrews")).setResultsName("lname")

full = fname + mname + lname + mname

output = full.parseString("Max Joseph Joseph Andrews Miller Miller").asDict()
print(output)
# {'fname': ['Max'], 'mname': [['Joseph', 'Joseph'], ['Miller', 'Miller']], 'lname': ['Andrews']}

, хотя в результате получается list из list с, а не один сплющенныйодин.

...