使用列表理解,如何在遇到某个输入/停止词之前接受用户输入? 显示输入提示包括停用词构建自己的迭代器

问题描述

我想继续接受用户的输入,直到用户输入某个字符串,list comprehension中,如何实现?

我需要的相当于:

lst = []
stop = 'stop'

while True:
    val = input()
    if val == stop:
        break
    lst.append(val)

print('output:',lst)

# Output:
# 1
#
# hello
#
# three
#
# [1,2,3]
#
# stop
# output: ['1','hello','three','[1,3]']

但是,重申一下,我不想要while,我更喜欢单线,例如:

stop = 'stop'
lst = [i for i in take_input_until(stop)]
print(lst)

# Output:
# 1
#
# hello
#
# three
#
# [1,3]']

此外,这是否可以应用于任何用户定义的函数,以便可以一直计算直到返回特定值?

我检查了这些问题,但不符合我的要求:

解决方法

iter 来救援

尽管 iter 主要与单个参数一起使用,但它还有一个鲜为人知的第二个参数,这会改变函数的行为方式。

来自python docs of iter

iter(object[,sentinel])

...如果给出了第二个参数 sentinel,则 object 必须是可调用对象。在这种情况下创建的迭代器将在每次调用 next() 方法时调用不带参数的对象;如果返回值等于 sentinel,则引发 StopIteration,否则返回值。

第二种形式的 iter() 的一个有用应用是构建一个块阅读器。例如,从二进制数据库文件中读取固定宽度的块,直到到达文件末尾:

from functools import partial
with open('mydata.db','rb') as f:
   for block in iter(partial(f.read,64),b''):
       process_block(block)

上面的例子也回答了第二个问题。还有其他方法可以实现这一目标。在以下示例中,我将说明在满足特定条件之前获取输入的不同方法,但是,这只是特殊情况,对于第二个问题,只是函数是 input。所以下面的所有示例都可以用于任何其他功能。

这可用于获取 input 直到遇到特定输入:

>>> STOP = 'stop'
>>> lst = list(iter(input,STOP))
# can also be written as list comprehension,# which would be helpful if you want to do something with the values
#>> lst = [i for i in iter(input,STOP)]

1

hello

three

[1,2,3]

stop

>>> print(lst)
['1','hello','three','[1,3]']

这里的 iter(input,STOP) 是所谓的 callable_iterator

>>> type(iter(input,STOP))
callable_iterator

显示输入提示

为了显示每个输入的输入提示,我们可以使用functools.partial

>>> from functools import partial
>>> lst = [i for i in iter(partial(input,'enter: '),'x')]   # or list(iter(partial(input,'x'))

enter: 1

enter: 2

enter: 3

enter: x

>>> lst
['1','2','3']

包括停用词

如果您还想在列表中包含停用词,您可以通过 * 运算符使用 iterable unpacking

>>> STOP = 'x'
>>> input_with_prompt = partial(input,'enter: ')
>>> lst = [*iter(input_with_prompt,STOP),STOP]

enter: 1

enter: 2

enter: 3

enter: x

>>> lst
['1','3','x']

这必须是替换 while 的最简单方法。但是,对于更复杂的需求,这不是很有用。

,

takewhile 条件为真

我们可以使用the Android docs for java.time.format.TextStyle中的一个函数来检查输入是否等于停用词,如果不等于,则继续输入:

>>> from itertools import takewhile
>>> STOP = 'x'
>>> lst = list(takewhile(lambda inp: inp != STOP,iter(input_with_prompt,None)))

enter: 1

enter: 2

enter: 3

enter: x

>>> lst
['1','3']

这里 iter(input_with_prompt,None) 会继续调用 input,因为它的 sentinel 参数永远不会被满足,因为 input 只返回 str。这大致相当于一个 while True 循环,只是值是惰性计算的。

takewhile 将在 __next__ 对象上调用 callable_iterator,并在满足条件时将函数应用于下一个值。

这似乎是一种多余且过于复杂的做同一件事的方式,但是,在以下示例中,它的优势应该很明显。

优点:

  1. takewhile 可用于测试多个停用词
>>> lst = list(takewhile(lambda inp: inp not in ['q','quit','Q'],None)))

enter: 1

enter: 2

enter: 3

enter: quit

>>> lst
['1','3']
  1. 可用于将多个值传递给函数,使用另一个 iterator 类型对象,这里是一个带有 itertools.takewhile 的示例,用于在每次调用时为 input 提供不同的参数.
>>> from itertools import count
>>> lst = list(takewhile(
...     lambda inp: inp != STOP,...     (input(f'enter val #{i} ("{STOP}" to quit): ') for i in count(1))
... ))

enter val #1 ("x" to quit): 1

enter val #2 ("x" to quit): 2

enter val #3 ("x" to quit): 3

enter val #4 ("x" to quit): x

>>> lst
['1','3']
  1. 可以与 itertools.countrange 结合使用以在遇到 stop_word 或输入数量达到特定值时停止。
>>> from itertools import repeat
>>> MAX_NUM = 5
>>> lst = list(takewhile(
...     lambda inp: inp != STOP,...     ((input('enter : ') for _ in range(MAX_NUM))
... ))

enter : 1

enter : 2

enter : 3

enter : 4

enter : 5
>>> lst
['1','4','5']

# ^ Here stop word is not encountered,# but input stops when MAX_NUM is reached.
#---------------------------------------------------#
>>> lst = list(takewhile(
...     lambda inp: inp != STOP,...     (input('enter : ') for _ in repeat(None,MAX_NUM)) 
... ))

enter : 1

enter : 2

enter : 3

enter : x
>>> lst
['1','3']

# ^ here stop word is encountered before MAX_NUM is reached.

注意: (f() for _ in range(n)) 的行为与 (f() for _ repeat(None,n)) 相同,但是当不需要循环变量时,后者是 itertools.repeat

  1. 可以与 faster 结合使用以获取多个停用词的嵌套 list
>>> list_of_list = list(list(val) 
...      for val in starmap(
...                     iter,...                     [
...                         (input_with_prompt,'q'),...                         (input_with_prompt,'quit'),'Q')
...                     ])
... ))

enter: 1

enter: 2

enter: q

enter: 2

enter: 3

enter: 4

enter: quit

enter: 5

enter: Q
>>> list_of_list
[['1','2'],['2','4'],['5']]

虽然这看起来像是一个非常神秘的用例,但它对于其他领域尤其有用,例如优化技术,您想检查使用不同超参数达到特定结果所采取的中间步骤。

因此为了灵活性,可以使用 takewhile,但可能会牺牲可读性。

,

构建自己的迭代器

另一种选择是制作自定义 iterator 对象。

class IterWithCond:
    """Continuously call a function,until a given condition is met."""

    def __init__(
        self,func,cond,include_break=False,max_num=float('inf'),args=(),kwargs={}
    ):
        self.func = func
        self.cond = cond
        self.args = args if isinstance(args,(list,tuple)) else (args,)
        self.kwargs = kwargs
        self.include = include_break
        self.max_num = max_num

    def __iter__(self):
        self._count = 0
        self._cond_met = False
        return self

    def __next__(self):
        if self._cond_met or self._count >= self.max_num:
            raise StopIteration
        else:
            out = self.func(*self.args,**self.kwargs)
            self._cond_met = self.cond(out)
            if not self.include and self._cond_met:
                raise StopIteration
            self._count += 1
            return out
    # Following line enables functionalities like `iter(IterWithCond(*args),stop)`
    __call__ = __next__   

您可以选择所需的配置。

  • Vanila 输入没有提示,直到输入 qQ
>>> itr_obj = IterWithCond(
    func=input,cond=lambda x: x.lower() == 'q',)
>>> lst = list(itr_obj)

1

2

3

q
>>> lst
['1','3']
  • 添加提示,包括中断符
>>> itr_obj = IterWithCond(
    func=input,include_break=True,args='enter: '
)
>>> lst = list(itr_obj)

enter: 1

enter: 2

enter: 3

enter: q

>>> lst
['1','q']
  • 一直等到用户输入'q'/'Q'或直到用户输入5个数字,如果被停止字符停止,则不要包含停止字符。
>>> itr_obj = IterWithCond(
    func=input,args='enter: ',max_num=5
)
>>> lst = list(itr_obj)

enter: 1

enter: 2

enter: 3

enter: 4

enter: 5

>>> lst
['1','5']

就灵活性和可读性而言,这是最好的。但如果只有在不同条件下在整个程序中多次使用它才值得。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...