来自无限集合的 Python argparse 选择 [英] Python argparse choices from an infinite set

查看:20
本文介绍了来自无限集合的 Python argparse 选择的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码来创建一个容器,它伪装成所有质数的集合(实际上隐藏了一个记忆的蛮力质数测试)

导入数学def is_prime(n):如果 n == 2 或 n == 3:返回真如果 n == 1 或 n % 2 == 0:返回错误别的:return all(n % i for i in xrange(3, int(1 + math.sqrt(n)), 2))类素数(对象):def __init__(self):self.memo = {}def __contains__(self, n):如果 n 不在 self.memo 中:self.memo[n] = is_prime(n)返回 self.memo[n]

到目前为止,这似乎有效:

<预><代码>>>>素数 = 素数()>>>7 素数真的>>>104729 素数真的>>>100 个素数错误的>>>100 不是质数真的

但是它与 argparse 的配合并不好:

<预><代码>>>>将 argparse 导入为 ap>>>解析器 = ap.ArgumentParser()>>>parser.add_argument('prime', type=int, choice=primes, metavar='p')_StoreAction(option_strings=[], dest='prime', nargs=None, const=None, default=None, type=<type 'int'>, options=<__main__.Primes object at 0x7f4e21783f10>, help=无,元变量 =p")>>>parser.parse_args(['7'])命名空间(素数=7)>>>parser.parse_args(['11'])命名空间(prime=11)>>>parser.parse_args(['12'])回溯(最近一次调用最后一次):文件<stdin>",第 1 行,在 <module> 中文件/usr/lib/python2.7/argparse.py",第 1688 行,在 parse_argsargs, argv = self.parse_known_args(args, namespace)文件/usr/lib/python2.7/argparse.py",第 1720 行,在 parse_known_args命名空间, args = self._parse_known_args(args, namespace)文件/usr/lib/python2.7/argparse.py",第 1929 行,在 _parse_known_args停止索引 = 消耗位置(开始索引)文件/usr/lib/python2.7/argparse.py",第1885行,在consume_positionalstake_action(动作,参数)文件/usr/lib/python2.7/argparse.py",第 1778 行,在 take_actionargument_values = self._get_values(action,argument_strings)_get_values 中的文件/usr/lib/python2.7/argparse.py",第 2219 行self._check_value(动作,值)_check_value 中的文件/usr/lib/python2.7/argparse.py",第 2267 行tup = value, ', '.join(map(repr, action.choices))类型错误:map() 的参数 2 必须支持迭代

docs 就是这么说的

<块引用>

任何支持 in 操作符的对象都可以作为选择传递value,所以dict对象,set对象,自定义容器等等都是支持.

显然我不想迭代无限的质数集合".那么为什么 argparse 试图map 我的素数呢?不是只需要innot in 吗?

解决方案

这是一个文档错误.这是 issue16468,由 PR 15566.

编写的库要求 choices 参数不仅是一个容器,而且是可迭代的,它试图列出可用的选项,这对您的情况不起作用.你可以尝试通过给它一个假的 __iter__ 来破解它,它只返回一些信息字符串.

I have the following code to create a container which pretends to behave like the set of all prime numbers (actually hides a memoised brute-force prime test)

import math

def is_prime(n):
    if n == 2 or n == 3:
        return True
    if n == 1 or n % 2 == 0:
        return False
    else:
        return all(n % i for i in xrange(3, int(1 + math.sqrt(n)), 2))


class Primes(object):

    def __init__(self):
        self.memo = {}

    def __contains__(self, n):
        if n not in self.memo:
            self.memo[n] = is_prime(n)
        return self.memo[n]

That seems to be working so far:

>>> primes = Primes()
>>> 7 in primes
True
>>> 104729 in primes
True
>>> 100 in primes
False
>>> 100 not in primes
True

But it's not playing nicely with argparse:

>>> import argparse as ap
>>> parser = ap.ArgumentParser()
>>> parser.add_argument('prime', type=int, choices=primes, metavar='p')
_StoreAction(option_strings=[], dest='prime', nargs=None, const=None, default=None, type=<type 'int'>, choices=<__main__.Primes object at 0x7f4e21783f10>, help=None, metavar='p')
>>> parser.parse_args(['7'])
Namespace(prime=7)
>>> parser.parse_args(['11'])
Namespace(prime=11)
>>> parser.parse_args(['12'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/argparse.py", line 1688, in parse_args
    args, argv = self.parse_known_args(args, namespace)
  File "/usr/lib/python2.7/argparse.py", line 1720, in parse_known_args
    namespace, args = self._parse_known_args(args, namespace)
  File "/usr/lib/python2.7/argparse.py", line 1929, in _parse_known_args
    stop_index = consume_positionals(start_index)
  File "/usr/lib/python2.7/argparse.py", line 1885, in consume_positionals
    take_action(action, args)
  File "/usr/lib/python2.7/argparse.py", line 1778, in take_action
    argument_values = self._get_values(action, argument_strings)
  File "/usr/lib/python2.7/argparse.py", line 2219, in _get_values
    self._check_value(action, value)
  File "/usr/lib/python2.7/argparse.py", line 2267, in _check_value
    tup = value, ', '.join(map(repr, action.choices))
TypeError: argument 2 to map() must support iteration

The docs just say that

Any object that supports the in operator can be passed as the choices value, so dict objects, set objects, custom containers, etc. are all supported.

Obviously I don't want to iterate the infinite "set" of primes. So why the heck is argparse trying to map my primes? Doesn't it just need in and not in?

解决方案

It was a documentation bug. This was issue16468, fixed in August 2019 by PR 15566.

The library as written requires the choices argument to be not just a container but also iterable, It tries to list the available options, which isn't going to work for your case. You could try to hack it by giving it a fake __iter__ that just returns some informational string.

这篇关于来自无限集合的 Python argparse 选择的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆