【问题标题】:How to parse optional and named arguments into list and dict?如何将可选参数和命名参数解析为列表和字典?
【发布时间】:2013-08-11 15:24:02
【问题描述】:

我想要一种紧凑的方法来解析以强制列表元素(未指定的数字)开头并使用= 以类似字典的定义结尾的单行字符串。

  • element-separator 应该是 , 并且 spaces 应该成为元素的一部分——我认为这排除了 shlex
  • 空格应该/可以在开始和结束(引号)
  • 如果元素包含,,则用户需要引用"
    • "key=value,with,comma"
    • key="value,with,comma" -- 任何更容易实现的方法
  • 错误的引用或包含引号字符的元素可以有未定义的行为。
  • 双键的行为也未定义。
  • 如果可以大大简化实现,则可以稍作改动。

让我们调用函数opts并让它返回一个list和一个dict

以下是一些输入示例和所需结果:

opts('dog,cat')                 # -> ["dog", "cat"], {}
opts('big fish,cat')            # -> ["big fish", "cat"], {}
opts('"a dog, a cat",a fish')   # -> ["a dog, a cat", "a fish"], {}
opts('key=value')               # -> [] {'key':'value'}
opts('key=the value,x=y')       # -> [] {'key':'the value', 'x':'y'}
opts('dog, big fish, eats="any, but peas", flies = no! '
   # -> ['dog','big fish'], {'eats':'any, but peas', 'flies':'no!' }

我忽略了shlexargparseoptparseconfigparser,我不知道该怎么处理这些。不过,我不确定 Regular Expressions 是否能解决这个问题。我认为json 的语法有点太严格了。和eval 一样,如果我更喜欢的话(因为它解析python ;-))

我在macro 中的手动解决方案不是很灵活,我希望将其参数处理替换为上述更通用的opts(s) 函数:

def macro(s):
    kw = { 'see':u"\\see", 'type':u"Chapter", 'title': u'??' }
    params = s.split(",")
    kw['label'] = params[0]
    if len(params) > 1:                   # very inflexible
        kw['title'] = params[1]
    for param in params[2:]:              # wrong if p[1] is already key=value
        key, value = param.split("=",1)  # doesn't handle anything, too simple
        kw[key] = value
    # ...rest of code...

目标是在此处使用可重用函数opts

def macro_see(s):
    ls, kw = opts(s)
    # ...rest of code...

【问题讨论】:

  • 你说元素分隔符应该是,,但你的第三个例子似乎暗示;也应该是一个分隔符。
  • @DSM 哎呀。我自己尝试的剩余物。已更正。谢谢
  • 如果有人想看看我如何集成提出的解决方案和一些演示,我把它们放在pastebin.com/BnyC48yH

标签: python parsing


【解决方案1】:

在这个解决方案中,opts 与 yuvi 的基本相同(添加了strip)。拆分器是shlex的自定义,使用posix模式处理引号。

def mylex(x):
    lex = shlex.shlex(x, posix=True)
    lex.whitespace = ','
    lex.whitespace_split = True
    return list(lex)

def opts(x):
    ll = []
    dd = {}
    items = mylex(x)
    for item in items:
        if '=' in item:
            k, v = item.split('=',1)
            dd[k.strip(' "')] = v.strip(' "')
        else:
            ll.append(item.strip(' "'))
    return (ll,dd)

通过了:

trials = [
    ['dog,cat',(["dog", "cat"], {})],
    ['big fish,cat',(["big fish", "cat"], {})],
    ['"a dog, a cat",a fish',(["a dog, a cat", "a fish"], {})],
    ['key=value',([], {'key':'value'})],
    ['key=the value,x=y',([], {'key':'the value', 'x':'y'})],
    ['dog, big fish, eats="any, but peas", flies = no!',(['dog','big fish'], {'eats':'any, but peas', 'flies':'no!' })],
]
for (x,y) in trials:
    print('%r'%x)
    args = opts(x)
    print(args)
    if args != y:
        print('error, %r'%y)
    print('')

【讨论】:

  • 这很好。它不需要预处理,也不需要缓慢/危险的评估。我会试试看。
  • shlex-解决方案相比的优势:更易于理解和维护。缺点:可能更难调整,因为shlex 太不灵活了——但也许不是,我总是可以像eval 解决方案一样使用re 进行预处理,并修改lexer。我会尝试一下,看看它随着时间的推移会如何。
【解决方案2】:

您可能想要创建自己的拆分函数,并带有一个在引入 " 时切换的标志。像这样:

def my_split(string, deli):
    res = []
    flag = True
    start = 0

    for i, c in enumerate(string):
        if c == '"':
            if flag:
                flag = False
            else:
                flag = True

        if c == deli and flag:
            res.append(string[start:i])
            start = i+1

    res.append(string[start:])
    return res

从那里开始,真的很容易:

def opts(s):
    items = map(lambda x: x.strip(), my_split(s, ','))

    # collect
    ls = []
    kw = {}
    for item in items:
        if '=' in item:
            k, v = item.split('=', 1)
            kw[k.strip()] = v.strip()
        else:
            ls.append(item)

    return ls, kw

这并不完美,您可能还需要做一些事情,但这绝对是一个开始。

【讨论】:

  • 加上.strip(' "') 去掉引号,这会产生所有的测试用例。 flag = not flag 也足够了。
【解决方案3】:

这是一种方法,我对输入进行按摩,使其符合 python 函数参数的语法要求,然后通过 eval 使用 python 解释器来解析它们。

import re
s = 'hog, "cog" , dog, bog, "big fish", eats="any, but peas", flies = "no!" '

# I think this will add quotes around any unquoted positional arguments
s = re.sub('(^|,)\ *([^\"\',\ ]+)\ *(?=,|$)', r'\1"\2"', s)

def f(*args, **kwargs):
    return (args, kwargs)

print eval("f("+s+")", {'f':f})

输出:

(('hog', 'cog', 'dog', 'bog', 'big fish'), {'flies': 'no!', 'eats': 'any, but peas'})

【讨论】:

  • 请注意,这要求关键字参数被引用为(eats="any, but peas",而不是eats=any, but peas)。
  • 添加了一个修复程序,以便处理符合规范的间距。
  • 我承认这有点邪恶:-)。我不会想到eval 的这种用法。正则表达式预处理也是我没有想到的。而'f:f` 是一个讨厌的把戏。我将您的建议扩展到pastebin.com/HaNqEq5S,主要是另一个预处理和更详细的变量名称
猜你喜欢
  • 2021-07-13
  • 2014-08-16
  • 2022-07-24
  • 2010-11-06
  • 2011-02-08
  • 1970-01-01
  • 1970-01-01
  • 2014-07-04
  • 2011-03-04
相关资源
最近更新 更多