I often find myself defining function args with list[SomeClass] type and think “do I really care that it’s a list? No, tuple or Generator is fine, too”. I then tend to use Iterable[SomeClass] or Collection[SomeClass]. But when it comes to str, I really don’t like that solution, because if you have this function:

def foo(bar: Collection[str]) -> None:
    pass

Then calling foo("hello") is fine, too, because “hello” is a collection of strings with length 1, which would not be fine if I just used list[str] in the first place. What would you do in a situation like this?

  • @[email protected]
    link
    fedilink
    6
    edit-2
    10 months ago

    I’d leave a docstring:

    def foo(bor: Iterable[str]) -> None:
        """foos bars by doing x and y to each bar"""
    

    Type hinting isn’t intended to prevent all classes of errors, it’s intended to provide documentation to the caller. Iterable[str] provides that documentation, and a docstring gives additional context if needed. If you want strict typing assurances, Python probably isn’t the tool you’re looking for.

    • 𝕨𝕒𝕤𝕒𝕓𝕚OP
      link
      fedilink
      210 months ago

      This + an assert seems like the way to go. I think that str should never have fulfilled these contracts in the first place and should have a .chars property that returns a list of one-character-strings. But this change would break existing code, so it is not going to happen.

      • @[email protected]
        link
        fedilink
        110 months ago

        IDK, I think strings being simple lists is less surprising than having a unique type. Most other languages model them that way, and it’s nice to be able to use regular list actions to interact with them.

        It’s really not something I’m likely to run into in practice. The only practical way I see messing this up is with untrusted inputs, but I sanitize those anyway.

  • Troy
    link
    fedilink
    210 months ago

    Kids these days and their type hinting. Back in my day, all objects were ducks, and we liked it!

  • @exussum
    link
    110 months ago

    deleted by creator

    • 𝕨𝕒𝕤𝕒𝕓𝕚OP
      link
      fedilink
      English
      3
      edit-2
      10 months ago

      I know that Iterable and Collection aren’t the same. My point is, that if you use Iterable[str] or Collection[str] as a more flexible alternative to list[str] you no longer have any type-hinting support protecting against passing in a plain string and you could end up with a subtle bug by unexpectedly looping over ['f', 'o', 'o'] instead of ['foo'].

  • @[email protected]
    link
    fedilink
    English
    110 months ago

    Look at the official docs. There is a table part way down stating which methods are available for each. I pick the one closest to how I use it. So if I’m not mutating I’ll use Sequence over List to inform the caller I’m treating as immutable and to safe guard myself from mutating it in my implementation via static type analysis.

    https://docs.python.org/3/library/collections.abc.html

    • 𝕨𝕒𝕤𝕒𝕓𝕚OP
      link
      fedilink
      210 months ago

      str matches most of these contracts, though, requiring additional checks if a str was passed or one of these collections containing strings.

  • @[email protected]
    link
    fedilink
    110 months ago

    I’m not sure why you wouldn’t just use packing to pass in a list of some objects that you need iterate over? Isn’t it normally bad form to pass lists as arguments? I feel like I’ve read this somewhere but can’t cite it

    • 𝕨𝕒𝕤𝕒𝕓𝕚OP
      link
      fedilink
      110 months ago

      Yes, that’s a good alternative for Collection[str] but not so much for Iterable[str] as you lose the lazyness of Generators.