3
\$\begingroup\$

This is a follow-up to Searching online English dictionaries:

import webbrowser
import random

available_dicts = [
    ('Merriam-Webster Dictionary', 'https://www.merriam-webster.com/dictionary/', '%20'),
    ('Oxford Dictionary', 'https://en.oxforddictionaries.com/definition/', '_'),
    ('Camberidge Dictionary', 'https://dictionary.cambridge.org/dictionary/english/', '-'),
    ('Collins Dictionary','https://www.collinsdictionary.com/dictionary/english/', '-'),
    ('Longman Dictionary of Contemporary English', 'https://www.ldoceonline.com/dictionary/', '-'),
    ('The American Heritage Dictionary of the English Language', 'https://ahdictionary.com/word/search.html?q=', '%20'),
    ('Almaany English-Arabic Dictionary', 'www.almaany.com/en/dict/ar-en/', '-'),
    ('Wordreference English-Arabic Dictionary', 'http://www.wordreference.com/enar/', '%20')
    ]

instructions = """
All of the ablove are the available online English dictionaries.
To select a dictionary, type its corresponding numeral.
To select more than one dictionary, seperate the numerals with ",".
To select all of them, type "*".
To let the program select a dictionary randomly, type "?".
Do not type the quotation marks.
Press Enter to exit.\n"""


class OnlineDictionary:
    """Represents an online dictionary.

    Attributes:
      name: str representing the dictionary's name
      url: str representing the URL used to look the query up
      sep: str used to seperate the parts of the query if there are white spaces between them
    """
    def __init__(self, name, url, sep):
        """Initializes an OnlineDictionary object.

        name: str
        url: str
        sep: str
        """
        self.name = name
        self.url = url
        self.sep = sep

    def search_for(self, query):
        """Searches the dictionary for a given query.

        query: str
        """
        query = self.sep.join(query.split())
        query_url = self.url + query
        webbrowser.open(query_url)


def print_data(available_dicts, instructions):
    """Prints the available dictionaries, their corresponding numerals and the instructions.

    available_dicts: list of tuples
    instructions: str
    """        
    for numeral, (name, _, _) in enumerate(available_dicts):
        print(numeral, name)

    print(instructions)     


def get_dicts(available_dicts):
    """Asks the user for which dictionary or dictionaries to use.

    available_dicts: list of tuples

    Returns: tuple
    """
    dicts_to_use = input('Specify which dictionary or dictionaries to use.\n')

    if dicts_to_use == '':
        exit()
    elif dicts_to_use == '*':
        dicts_to_use = available_dicts
    elif dicts_to_use == '?':
        dicts_to_use = [random.choice(available_dicts)]
    else:
        try:
            dicts_to_use = dicts_to_use.replace(' ', '').split(',')
            numerals = []

            for num in dicts_to_use:
                num = int(num)
                if num not in range(len(available_dicts)):
                    raise ValueError        
                numerals.append(num)

            dicts_to_use = [available_dicts[num] for num in set(numerals)]  # set class is used to get rid of duplicates

        except ValueError:
            return get_dicts(available_dicts)

    return dicts_to_use


def use_continuous_mode():
    "Asks the user for what mode to use."
    message = """In Continuous Mode the selected dictionary or dictionaries are
used for your next queries without asking you to select any again.
Do you want to use it? Type "Yes" or "No", ommiting the quotation marks.\n"""

    mode = input(message).lower()

    return exit() if mode == '' else True if mode == 'yes' else False if mode == 'no' else use_continuous_mode()


def get_query():
    """Asks the user for what to search for."""
    query = input('Type something to search for.\n')

    return exit() if query == '' else query


def search_selected_dicts(selected_dicts, query):
    """Searches the selected dictionaries for the given query
    by making a list of OnlineDictionary objects out of them.

    selected_dicts: list of tuples
    query: str
    """
    online_dicts = [OnlineDictionary(name, url, sep) for name, url, sep in selected_dicts]

    for online_dic in online_dicts:         
        print('Searching %s for "%s"...' % (online_dic.name, query))
        online_dic.search_for(query)

    print('\nFinished searching. Check your Web browser.\n')


def main():      
    print_data(available_dicts, instructions)

    continuous_mode = use_continuous_mode()

    if continuous_mode:
        selected_dicts = get_dicts(available_dicts)

        while True:
            query = get_query()
            print()
            search_selected_dicts(selected_dicts, query)
    else:
        while True:
            selected_dicts, query = get_dicts(available_dicts), get_query()
            print()
            search_selected_dicts(selected_dicts, query)


if __name__ == '__main__':
    main()
\$\endgroup\$
3
\$\begingroup\$

you can change your structure from

('Merriam-Webster Dictionary', 'https://www.merriam-webster.com/dictionary/', '%20'),

to

{
    'name':'Merriam-Webster Dictionary',
    'url':'https://www.merriam-webster.com/dictionary/',
    'sep':'%20'
},

+1 usage of main

you might also considering the use of a class to group the methods, that will allow for better representations, without the need for the print() in

    while True:
        query = get_query()
        print()
        search_selected_dicts(selected_dicts, query)

else i feel that that logic

if continuous_mode:
    selected_dicts = get_dicts(available_dicts)

    while True:
        query = get_query()
        print()
        search_selected_dicts(selected_dicts, query)
else:
    while True:
        selected_dicts, query = get_dicts(available_dicts), get_query()
        print()
        search_selected_dicts(selected_dicts, query)

is a bit too much in main. main's content should be relective of the work done when running the file, at a glance, the work done should be deduced. if while seeing the main the reader has to figure out what's going on, you might consider simplification

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Thanks for your review! Can you explain please how does changing the structure from ('Merriam-Webster Dictionary', 'https://www.merriam-webster.com/dictionary/', '%20') to {'name':'Merriam-Webster Dictionary', 'url':'https://www.merriam-webster.com/dictionary/', 'sep':'%20'} improve the code? What do you mean by "+1 usage of main", do you mean that I should include available_dicts in main? \$\endgroup\$ Aug 10 '18 at 19:55
  • 1
    \$\begingroup\$ 1) structure change : no need for tuple[0], you get it by dict['name'], more explicit while reading code. even if you don't use an intermediary value no problem. also, if you want nine informations, you'll still be fine. no need for tuple[8]. 2) usage of main : usage of a main function is recommended to catch erros more easily. 3) in main: no, just in main you include functions, methods, rather than some ifs etc. in main, the user should get an idea what is happening overall rather than following the logic. just wrap that code section \$\endgroup\$ Aug 11 '18 at 6:16
1
\$\begingroup\$

Contrary to my answer to your original question (item 3), you should use break and continue as in your original code for get_dicts, instead of tail recursion. Python does not optimise tail recursion, so the current code could cause a RecursionError: maximum recursion depth exceeded. Sorry for the misleading advice!

\$\endgroup\$
2
  • \$\begingroup\$ Thanks for coming back and pointing this out. I will take this into account when refactoring the code. \$\endgroup\$ Nov 21 '18 at 22:29
  • \$\begingroup\$ Glad I could help! \$\endgroup\$ Nov 21 '18 at 22:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.