0
\$\begingroup\$

I'm unsure if this question belongs on Code Review or Stack Overflow.

Another developer told me that I should consider adding type hints to my open source project WordHoard.

I have never used type hints before and the documentation doesn't seem intuitive. So I need to guidance on how to implement this functionality in my code.

I've started looking at adding it some low level code pieces first.

For example:

from typing import AnyStr, Dict

def colorized_text(r: int, g: int, b: int, text: str) -> AnyStr:
    """
    This function provides error messages color.
    For example:
    rgb(255, 0, 0) is displayed as the color red
    rgb(0, 255, 0) is displayed as the color green
    :param r: red color value
    :param g: green color value
    :param b: below color value
    :param text: text to colorized
    :return: string of colorized text
    """
    return f"\033[38;2;{r};{g};{b}m{text}\033[0m"

Are type hints implemented correctly in the function colorized_text?

Here is another code example.

from typing import Optional

temporary_dict_antonyms = {}

def cache_antonyms(word: str) -> [bool, str]:
    item_to_check = word
    if item_to_check in temporary_dict_antonyms.keys():
        values = temporary_dict_antonyms.get(item_to_check)
        return True, list(sorted(set(values)))
    else:
        return False, None

def insert_word_cache_antonyms(word: str, values: list[str]) -> None:
    if word in temporary_dict_antonyms:
        deduplicated_values = set(values) - set(temporary_dict_antonyms.get(word))
        temporary_dict_antonyms[word].extend(deduplicated_values)
    else:
        values = [value.strip() for value in values]
        temporary_dict_antonyms[word] = values

How do I implement type hints correctly in the second example?

Thanks in advance for any guidance!

\$\endgroup\$
6
  • 2
    \$\begingroup\$ First example works, is almost correct, and can be reviewed. Second example needs to be cut away to Stack Overflow. \$\endgroup\$
    – Reinderien
    Feb 15, 2023 at 17:05
  • 1
    \$\begingroup\$ I am unwilling to believe you meant .append(values). It seems more likely you intended .extend(values), leading to a single flat list of antonyms, rather than a nested list structure. \$\endgroup\$
    – J_H
    Feb 15, 2023 at 17:20
  • \$\begingroup\$ @J_H GOOD CATCH!! You found an issue that I didn't know existed. \$\endgroup\$ Feb 15, 2023 at 17:52
  • \$\begingroup\$ @Reinderien I updated the second code example so that it doesn't throw an error now. \$\endgroup\$ Feb 16, 2023 at 14:05
  • 2
    \$\begingroup\$ mypy can help you check if your type hints are correct. \$\endgroup\$
    – Wombatz
    Feb 16, 2023 at 14:13

1 Answer 1

2
\$\begingroup\$

Are type hints implemented correctly in the function colorized_text?

Almost; the return type should just be str, not AnyStr. AnyStr is for when (a) a function can take or return both str and bytes, (b) there are either multiple AnyStr arguments or one (or more) AnyStr arguments & an AnyStr return type, and (c) all AnyStr arguments/return types must be either all strs or all bytess when the function is actually called. A simple example would be a function that take two strs or two bytes and returns their concatenation; the arguments and return type would then all be AnyStr.

Also, you imported typing.Dict but don't use it.

How do I implement type hints correctly in the second example?

First, you need an annotation for temporary_dict_antonyms. It appears to be a dict mapping strs to lists of strs, so write temporary_dict_antonyms: dict[str, list[str]] = {}.

Secondly, to annotate a tuple (such as in cache_antonyms's return type), you need to write either tuple[ArgType1, ArgType2] etc. or tuple[ArgType, ...] for a tuple of variable length where all elements are the same type.

Now, cache_antonyms can return either a tuple of a bool and a list[str] or a bool and a None. I don't know why you feel the need to return a bool when the caller could just check whether the second field is non-None, but the simplest way to annotate the return value is -> tuple[bool, Optional[list[str]]].

By the way, I would recommend implementing cache_antonyms with a single dict query as follows:

def cache_antonyms(word: str) -> tuple[bool, Optional[list[str]]]:
    try:
        values = temporary_dict_antonyms[word]
    except KeyError:
        return (False, None)
    else:
        return (True, sorted(set(values)))  # sorted() already returns a list

The annotations on insert_word_cache_antonyms are fine, but mypy will likely complain about set(temporary_dict_antonyms.get(word)), because the get() can return None, which is not a valid input to set(); change it to set(temporary_dict_antonyms[word]).

Additional comments:

  • If you want your code to run on Pythons before 3.9, then any parametrized dict, list, tuple, etc. in your annotations need to be swapped out for typing.Dict etc. — or, alternatively, just add from __future__ import annotations to the top of your code.

  • Since the values of temporary_dict_antonyms are always converted to sets after retrieval, you should probably just store sets in the dict in the first place instead.

  • In insert_word_cache_antonyms, you strip() the elements of values when the word is not in the dict, but you don't do it when adding to a pre-existing collection of values; are you sure that's what you want?

\$\endgroup\$
11
  • \$\begingroup\$ Thanks for the reply. When I exchanged your cache_antonyms code with mine I get an error Unresolved reference 'word' \$\endgroup\$ Feb 16, 2023 at 17:01
  • \$\begingroup\$ Also I use the 'strip()' function to remove the leading and trailing spaces from the strings in the first list. I could move this strip() to the function (scraper) that creates this list. \$\endgroup\$ Feb 16, 2023 at 17:04
  • \$\begingroup\$ @Lifeiscomplex: I believe the error was caused by a missing ] in the return type annotation. Try now. // Regarding the strip(), my point was more that it seems odd and inconsistent to only strip the first batch of values added for a word but not any subsequent batches. \$\endgroup\$
    – jwodder
    Feb 16, 2023 at 17:40
  • \$\begingroup\$ Why is this correct cache_antonyms(word: str) -> tuple[bool, Optional[list[str]]] using type hints and this isn't cache_antonyms(word: str) -> [bool, str]? \$\endgroup\$ Feb 17, 2023 at 15:28
  • 1
    \$\begingroup\$ Union[str, list[str]] would mean that temporary_dict_antonyms was either a str or a list of str, which is not right at all for this case. It's a dict, so you need to declare it as dict[KeyType, ValueType]; in this case, the dict maps strs to lists of strs, so dict[str, list[str]]. \$\endgroup\$
    – jwodder
    Feb 17, 2023 at 21:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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