简体   繁体   English

引用列表索引来枚举值

[英]Reference list index to enumerate value

I have a program which checks an outlook inbox for attachments from specific senders in a dictionary.我有一个程序可以检查 Outlook 收件箱中是否有来自字典中特定发件人的附件。 Python - Outlook search emails within specific range only Python - Outlook 仅在特定范围内搜索电子邮件

That program works fine, and now I'm trying to expand on it by giving the user the ability to choose a specific sender from the dictionary, so as to only search their emails.该程序运行良好,现在我试图通过让用户能够从字典中选择特定发件人来扩展它,以便只搜索他们的电子邮件。 This code is within the previous program.这段代码在之前的程序中。

The dictionary I have is senderDict = {sender@address.com : Sender Name}我的字典是senderDict = {sender@address.com : Sender Name}

To turn it into a menu of values to choose from I've used enumerate:要将其转换为可供选择的值菜单,我使用了 enumerate:

listSender = list(senderDict.values())

for count, item in enumerate(listSender,1):
    print(count, item)

print("There are", len(listSender), "items.")

while True:
    senderNum = int(input("Choose a sender (zero for any): "))        
    try:
        if senderNum <= len(listSender) and senderNum >= 0:
            #senderNum = int(input("Choose a sender (zero for any): "))
            print("you chose", listSender[senderNum])
            break

        elif senderNum == 0:
            print("Searching for any sender")
            break

        elif senderNum < 0:
            print("Cannot choose a negative number.")
            continue

    except ValueError: #Not int
        print("You did not enter a valid number (ValueError)")
        input()

    except IndexError: #outside range
        print("You did not enter a valid number (IndexError)")
        input()

The issue is that choosing zero will choose the zero index, instead of 'search any'.问题是选择零将选择零索引,而不是“搜索任何”。 How do I make the list index match the enumerate values?如何使列表索引与枚举值匹配?

Also, entering a non-number or blank input crashes the program, so I'm not certain what exception covers that, I thought it fell under ValueError.此外,输入非数字或空白输入会使程序崩溃,所以我不确定哪些异常涵盖了它,我认为它属于 ValueError。

I'm not really sure if I even need to turn the dictionary into a list, but I had read that it was necessary for enumerate to work so I just went with it.我什至不确定是否需要将字典转换为列表,但我读到枚举工作是必要的,所以我就这样做了。

it seems you have a little bug in your code!您的代码中似乎有一个小错误!

if senderNum <= len(listSender) and senderNum >= 0:

The above line includes 0. 0 >= 0 == True therefore上面的行包括 0. 0 >= 0 == True因此

elif senderNum == 0:

will never be reached since it is already reached in the previous if statement.永远不会到达,因为它已经在之前的 if 语句中到达了。 I believe what you wanted to do is我相信你想做的是

if senderNum <= len(listSender) and senderNum > 0:

This way the above line will be False when the chosen number is 0 and the elif line will be reached by your code.这样,当所选数字为 0 时,上面的行将为 False,并且您的代码将到达 elif 行。 If you need it to match an index in your list, since you set your enumerate to start at 1 you can manually subtract 1 from the user's chosen value count - 1 .如果您需要它来匹配列表中的索引,由于您将枚举设置为从 1 开始,您可以手动从用户选择的值count - 1减去count - 1

In order to catch incorrect values you could add an else statement:为了捕获不正确的值,您可以添加一个 else 语句:

else:
    print('Incorrect value')
    continue

Your question is how to list index match the enumerate values.您的问题是如何列出索引匹配枚举值。 That is quite easy you only have to subtract 1 from the input your user provides you.这很容易,您只需从用户提供给您的输入中减去 1。 The problem is in your if clause senderNum >= 0 that will be true if your user inputs 0 so the elif senderNum == 0 will never be true问题在于您的 if 子句senderNum >= 0如果您的用户输入 0 elif senderNum == 0真,因此elif senderNum == 0永远不会为真

For your crash when you enter a non-number that happens because you have the try after the conversion of the entry into an int.当您输入一个非数字时发生崩溃,因为您在将条目转换为 int 后进行了try So you have to put the try befor the int(input("Choose a sender (zero for any): "))所以你必须把 try 放在int(input("Choose a sender (zero for any): "))

My suggestion is rather to choose negative number for sending to any.我的建议是选择负数发送给任何人。

So my suggestion is the following:所以我的建议如下:

listSender = list(senderDict.values())

for count, item in enumerate(listSender):
        print(count, item)

print("There are", len(listSender), "items.")

while True:
    try:  # have to be before the int(input(...)) to catch the exception
        senderNum = int(input("Choose a sender (-1 for any): "))        

        if senderNum < len(listSender) and senderNum >= 0: # len(listSender) is not allowed because the index starts at 0 and the last entry is len(listSender) -1                    
            print("you chose", listSender[senderNum])
            break

        elif senderNum < 0:
            print("Search for any sender")
            break

    except ValueError: #Not int
        print("You did not enter a valid number (ValueError)")
        input()

    except IndexError: #outside range
        print("You did not enter a valid number (IndexError)")
        input()

if you really want to use 0 as for send any I then you have to do the following:如果您真的想使用 0 作为发送任何 I 那么您必须执行以下操作:

listSender = list(senderDict.values())

for count, item in enumerate(listSender,1):
        print(count, item) 

print("There are", len(listSender), "items.")

while True:
    try:
        senderNum = int(input("Choose a sender (zero for any): "))
        if senderNum <= len(listSender) and senderNum > 0: # if senderNum is zero then send for any so senderNum >= 0 has to be changed to senderNum > 0
            print("you chose", listSender[senderNum-1]) # because index starts with 0 you have to subtract 1 from the input
            break

        elif senderNum == 0:
            print("Searching for any sender")
            break

        elif senderNum < 0:
            print("Cannot choose a negative number.")
            continue

    except ValueError: #Not int
        print("You did not enter a valid number (ValueError)")
        input()

    except IndexError: #outside range
        print("You did not enter a valid number (IndexError)")
        input()

To address your last question about enumerate and dictionary/list.解决您关于枚举和字典/列表的最后一个问题。 A dictionary is not ordered so that is why you can't enumerate through it with an index.字典没有排序,所以这就是为什么你不能用索引枚举它的原因。 So it is necessary to do a conversion into a list.所以有必要做一个转换成一个列表。 The other way to use it without casting it into a list is to use the dictionary keys as input instead of index numbers.使用它而不将其转换为列表的另一种方法是使用字典键而不是索引号作为输入。

The issue is that choosing zero will choose the zero index, instead of 'search any'.问题是选择零将选择零索引,而不是“搜索任何”。 How do I make the list index match the enumerate values?如何使列表索引与枚举值匹配?

First fix the order of your tests, then substract 1 from the user's input.首先修复测试的顺序,然后从用户的输入中减去 1。

Also, entering a non-number or blank input crashes the program, so I'm not certain what exception covers that, I thought it fell under ValueError.此外,输入非数字或空白输入会使程序崩溃,所以我不确定哪些异常涵盖了它,我认为它属于 ValueError。

int(something) can raise either a TypeError (if type(something) isn't something that can be interpreted as numeric value) or ValueError if the type is ok but not the value. int(something)可以引发TypeError (如果type(something)不是可以解释为数值的东西)或ValueError如果类型正常但不是值。

input() ( raw_input() in py2) always returns a string so you cannot have a TypeError eher and ValueError` is indeed what you want to catch - but you have to put your try/except block at the right place. input() ( py2 中的raw_input()always returns a string so you cannot have a TypeError eher and ValueError` 确实是你想要捕捉的 - 但你必须把你的 try/except 块放在正确的位置。 Currently you have:目前你有:

senderNum = int(input("Choose a sender (zero for any): "))        
try:
  # use senderNum here
except ValueError:
  # ...

but the ValueError is raised by int(...) so it will indeed not be caught.但是ValueError是由int(...)引发的,因此它确实不会被捕获。 You want:你要:

try:
   senderNum = int(input("Choose a sender (zero for any): "))        
except ValueError:
   print("not a valid value")
   continue

# ok, now `senderNum` is an integer

Also since you test that senderNum is within boundaries, you should not have any IndexError .此外,由于您测试senderNum是否在边界内,因此您不应该有任何IndexError Well, actually you wouldn't if the test was correct - you should test that senderNum is strictly smaller than len(listSender) - remember that lists are zero-based, so the last index of a list is len(lst) - 1 :好吧,实际上如果测试是正确的,你就不会 - 你应该测试senderNum严格小于len(listSender) - 记住列表是从零开始的,所以列表的最后一个索引是len(lst) - 1

>>> lst = list("abc")
>>> len(lst)
3
>>> 3 <= len(lst)
True
>>> lst[3]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range
>>> 3 < len(lst)
False
>>> 

I'm not really sure if I even need to turn the dictionary into a list, but I had read that it was necessary for enumerate to work so I just went with it.我什至不确定是否需要将字典转换为列表,但我读到枚举工作是必要的,所以我就这样做了。

enumerate() works on any iterable. enumerate()适用于任何可迭代对象。 A dict is an iterable so you could just pass senderDict to enumerate directly - but then you'd have the keys, not names. dict是可迭代的,因此您可以直接通过senderDict进行enumerate -但是您将拥有密钥,而不是名称。 Or you could just pass senderDict.values() directly, but then you're going to have another issue, which is how to get the email back from the name.或者您可以直接传递senderDict.values() ,但是您将遇到另一个问题,即如何从名称中取回电子邮件。

The proper solution here is to turn your dict into a list of (key, value) tuples - which is as simple as list(senderDict.items()) , so you can get both the name and email from the choosen list index.此处正确的解决方案是将您的 dict 转换为(key, value)元组list(senderDict.items()) - 这与list(senderDict.items())一样简单,因此您可以从选择的列表索引中获取姓名和电子邮件。

Corrected code:更正的代码:

senders = list(senderDict.items())

# this won't change during execution so let's 
# stop recomputing it each time

nb_senders = len(senders)

for index, (email, name) in enumerate(senders, 1):
    print("{}: {} ({})".format(index, name, email)


print("There are {} items.".format(nb_senders))


while True:
    # get the raw input
    senderNum = input("Choose a sender (zero for any): ")        

    # now validate it
    try:
        senderNum = int(senderNum)
        if senderNum < 0:
            raise ValueError("should not be negative")
        if senderNum > nb_senders:
            raise ValueError("should be lesser than {}".format(nb_senders))

    except ValueError as e:
        print("'{}' is not valid choice: {}".format(senderNum,  e))
        continue

    # ok, done           
    if senderNum == 0:
        print("Searching for any sender")
        break

    print("you chose {} ({})".format(*listSender[senderNum]))
    break

Thanks for all the helpful responses!感谢所有有用的回复!

Instead of trying to change the value of the input, I decided to insert a list option for 'Search Any' after converting the dictionary to a list, then let enumerate start from zero.我没有尝试更改输入的值,而是决定在将字典转换为列表后为“搜索任何”插入一个列表选项,然后让枚举从零开始。 This way I don't have to do any addition or subtraction and the index values still match the input.这样我就不必做任何加法或减法,索引值仍然与输入匹配。

listSender = list(senderDict.values())
listSender.insert(0, "Search Any")

for count, item in enumerate(listSender):
    print(count, item)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM