简体   繁体   中英

Interactively selecting items from a list

I have a need to print the contents of a folder to a user, who then picks one of the files. The program then takes that choice and uses it elsewhere.

My code to achieve this:

keylist = os.listdir('./SomeFolder/')
keycount = len(keylist)
for key in keylist:
    keynum = (keylist.index(key) + 1)
    print(str(keynum) + ' : ' + str(key))
while True:
    try:
        keychoice = int(input('Please Choose a Key : '))
        print()
        if 1 <= keychoice <= keycount:
            break
        else:
            raise MenuError()
    except ValueError:
        print('Please enter a valid option')
        continue
    except MenuError:
        print('Please enter a valid option, ' + str(keychoice) + " isn't an option.")
        continue

chosenkey = keylist[(keychoice - 1)]
print(chosenkey + ' has been chosen')

Overall, this works ok. But, is there a better way to do it?

I'm new to python, so any pointers are greatly received. The program in which i use this is pySignare Thanks.

I'll take a closer look at this shortly, but the thing that jumped out at me was this:

for key in keylist:
    keynum = (keylist.index(key) + 1)
    print(str(keynum) + ' : ' + str(key))

You're using list.index to get do a lookup of a key while you're iterating through it. Don't do that.

The old better way of doing that was this:

for i in range(len(keylist)):
    key = keylist[i]
    keynum = i
    print("{} : {}".format(keynum,key))

The REAL way you should do this is:

for keynum, key in enumerate(keylist):
    print("{} : {}".format(keynum + 1,key))

enumerate(iterable) runs through iterable and returns a tuple for each item, representing (index, value) . You're unpacking those with keynum, key . In order to fit what you did originally, you add one to keynum before displaying it, then you'll subtract one again from the user input.

I would also make a function that defines integer input. Something like:

def int_input(prompt,range_=None):
    while True:
        in_ = input(prompt)
        try: in_ = int(in_)
        except ValueError: pass
        else:
            if not range or if in_ in list(range_): return in_
        print("Invalid value, please try again.")

This will let you do something like:

keylist = os.listdir("path/to/dir")
for i,key in enumerate(keylist):
    print("{} : {}".format(keynum+1, key))
chosen_key = int_input("Which dir? ",range(1,len(keylist)+1)) - 1
print ("{} has been chosen!".format(keylist[chosen_key]))

The only improvement I can offer is to use the enumerate() function :

keylist = os.listdir('./SomeFolder/')
keycount = len(keylist)

for keynum, key in enumerate(keylist, 1):
    print('{} : {}'.format(keynum, key))

I started the count for you at 1 to match your output; users find counting from 1 more natural anyway.

This also makes use of string formatting , a much more readable and concise method of building strings.

The two main things you do here that are worth avoiding are the while True loop and the lack of functions. In general as well, I think you can accomplish what you are looking for much easier:

def getChoice(path):
    file_list = os.listdir(path)
    for i,j in enumerate(file_list):
        print "{choice}: {f}".format(choice=i+1, f=j) #Index from one.
    user_choice = int(input("What file do you wish to use?"))

    if user_choice in range(len(file_list)):
        print "You chose {f}".format(f=file_list[user_choice - 1])
        return file_list[user_choice-1]
    else:
        print "That is not a valid selection."
        return False

Then I'd wrap the function call so I had something like:

choice = False
while not choice:
    choice = getChoice(path)

This way you avoid a while true loop, and your getChoice function is independently testable of anything else.

Note: I didn't test this code, but it looks good.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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