3
\$\begingroup\$
def parse_names(tex_names: list[str]) -> set[str]:
    # the list comprehension in question
    return set(parsed_name for parsed_name in [_parse_name(tex_name) for tex_name in tex_names] if parsed_name)


def _parse_name(tex_name: str) -> Optional[str]:
    tex_regex = re.compile(r"^%? ?([\w() äöüÄÖÜß]+)( \((ab|bis) \d{2}:\d{2} Uhr\))?,")
    match = tex_regex.match(tex_name)
    if match:
        return match.groups()[0]
    return None

with test

def test_parse_names():
    raw_names = [
        "% Valid Name,",
        " Without percent,",
        "Without space,",
        "% Another (valid) name,",
        "% Max Mustermann (ab 13:00 Uhr),",
        "% Maxi Mustermann (bis 14:00 Uhr),",
        "% Mara Musterfrau (von 13:00 Uhr),",  # invalid
        "% Lara Musterfrau von 13:00 Uhr,",  # invalid
        "12345",  # invalid
        "%% some message",  # invalid
        "Missing comma",  # invalid
    ]

    expected = {
        "Valid Name",
        "Without percent",
        "Without space",
        "Another (valid) name",
        "Max Mustermann",
        "Maxi Mustermann"
    }
    actual = parse_names(raw_names)
    assert actual == expected

Is it possible to simplify the list comprehension while maintaining correct typing? E.g.

def parse_names(tex_names: list[str]) -> set[str]:
    return set(_parse_name(tex_name) for tex_name in tex_names if _parse_name(tex_name))

works just fine, but triggers the mypy error

error: List comprehension has incompatible type List[Optional[str]]; expected List[str]
\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

Mypy doesn't know that calling _parse_name multiple times will always return the same value if the same value is passed to it. You could try using walrus operator so it uses the value returned specifically for the truthiness check:

return set(value for tex_name in tex_names if (value := _parse_name(tex_name)))

This is also better because it avoids needing to call the function twice.

However, since all you're doing is passing each value to a function and checking the returned value's truthiness, you could use map and filter instead:

return set(filter(None, map(_parse_name, tex_names)))
\$\endgroup\$

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.