Live data from Hacker News

Python Type Hints – *args and **kwargs (2021)

adamj.eu

151–158 of 158 posts

Re: Python Type Hints – *args and **kwargs (2021)

#151

Earlier quoted context omitted.

I linked directly to the section of mypy docs that shows you are wrong - all you have to do is click the link to understand that you are wrong: >(Using stub file syntax at runtime). You may also occasionally need to elide actual logic in regular Python code... You can also elide default arguments as long as the function body also contains no runtime logic: the function body only contains a single ellipsis, the pass s…

And why would I care about mypy docs? pyright does the correct thing by showing an error. In any case, the second example is definitely not a stub.

this is the weirdest "head in sand" moment; you literally called out mypy as a desirable alternative

>And to add insult to injury Pycharm doesn't even have a pyright plugin and the mypy plugin is extremely slow and buggy.

ie you recognize that aligning with mypy is desirable.

Re: Python Type Hints – *args and **kwargs (2021)

#152
post #112

Earlier quoted context omitted.

It’s sad to see this happen to be honest. Seems like Jet Brains is getting distracted from their core value proposition: good IDEs. If electron based IDEs are becoming more responsive and performant than their “native” IDEs they have major priority problems.

What even distracts them? IDEs are supposedly the only thing they do. Well, maybe except for Kotlin. And it's not like their IDEs are very cheap either. I mean, not that cheep that I'd like the idea of being too much mentally invested into something, that barely competes with a free source-code editor, let alone lags behind it.

There is Fleet, which they purport to be their next gen IDE. Which I haven’t even tried, though I am am avid pycharm user, so maybe it’s not getting the results they hope for?

Re: Python Type Hints – *args and **kwargs (2021)

#153

Earlier quoted context omitted.

I agree. It adds all the inconvenience of static typing with none of the benefits.

> none of the benefits Autocomplete and type-checking are massive boons to writing "type-correct" code, fast. It doesn't guarantee that your code won't explode at runtime or is logically correct (that's what tests are for), but it does help eliminate an entire class of bugs, and, again, speeds up development a massive amount when dealing with very large codebases.

[deleted]

Re: Python Type Hints – *args and **kwargs (2021)

#154

Earlier quoted context omitted.

At an even more basic level, the lack of static typing seems like such a tradeoff getting an incredibly huge nuisance in readability and stupid runtime bugs that shouldn't be a thing in exchange for a feature that's rarely useful. Granted, I'm primarily an embedded developer. Can any Python experts explain to me a highly impactful benefit of dynamic typing?

For small programs, dynamic typing can be faster to write (not read). As soon as your program grows: "uh oh". Once you add maintenance into the cost equation, dynamic typing is a huge negative. To be fair: 15 years ago, people were writing a lot of Java code that effectively used dynamic typing by passing around Object references, then casting to some type (unknowable to the reader) when using. (C#: Same.) It was inf…

I'm not sold on this. Often I type the output I want to get, and reverse the code to get there. and that's faster because it's now all auto completing.

Re: Python Type Hints – *args and **kwargs (2021)

#155

Earlier quoted context omitted.

For small programs, dynamic typing can be faster to write (not read). As soon as your program grows: "uh oh". Once you add maintenance into the cost equation, dynamic typing is a huge negative. To be fair: 15 years ago, people were writing a lot of Java code that effectively used dynamic typing by passing around Object references, then casting to some type (unknowable to the reader) when using. (C#: Same.) It was inf…

I'm not sold on this. Often I type the output I want to get, and reverse the code to get there. and that's faster because it's now all auto completing.

Interesting point. What language?

Re: Python Type Hints – *args and **kwargs (2021)

#156

Earlier quoted context omitted.

I'm not sold on this. Often I type the output I want to get, and reverse the code to get there. and that's faster because it's now all auto completing.

Interesting point. What language?

That's been my experience of powershell and typescript. To a lesser extreme python because its type hints are a bit crap.

Though I can see why you might not agree after trying an extreme like Rust. Sometimes I want to run a broken program to stop the debugger and see what I'm dealing with and rust won't do that.

Re: Python Type Hints – *args and **kwargs (2021)

#157
post #16

Earlier quoted context omitted.

Sadly a problem with any wrapper function is that it nullifies this kind of information. Use functools.wraps.

My question is that can @warps warp more than 1 function? Maybe in some use case people need to merge 2 functions into 1, I don't know if it can handle this situation.

[deleted]

Re: Python Type Hints – *args and **kwargs (2021)

#158

Earlier quoted context omitted.

> I'm not sure what it means to "merge two functions into one", can you elaborate? I'm not OP, but I see this pattern often enough: def foo(**kwargs): pass der bar(**kwargs): pass def wrapper(**kwargs): foo(**kwargs) bar(**kwargs)

Yup, this exactly.

That makes sense. If you have two functions with identical type signatures, then you should be able to invoke @functools.wraps (or its underlying functools.update_wrapper) such that the annotations are propagated correctly. In this case, that might be as simple as "functools.wraps(assigned=('__annotations__'))".

The rest of what "wraps" does isn't really applicable to what you're trying to do, though: propagating __doc__, __name__ or __module__ etc. isn't really meaningful when combining two functions with different names, as toxik points out.

Coming at it from the other side, you can use typing.ParamSpecs to write a higher-order "wrapper()" that wraps 2 arbitrary functions with identical signatures, like this:

    S = typing.ParamSpec('S')
    R = typing.TypeVar('R')
    def wrapper(func1: typing.Callable[S, R], func2: typing.Callable[S, R]) -> typing.Callable[S, typing.Tuple[R, R]]:
        def inner(**kwargs):
            return func1(**kwargs), func2(**kwargs)
        return inner

You can additionally use typing.Concatenate to indicate additive modifications to those signatures if the two inner signatures aren't the same.

However, what I thought you meant originally is the common problem of deduplicating large/complex function type signatures so you don't have to write them out multiple times or risk drift. In the parent post, consider what would happen if the signatures of "wrapper", "foo", and "bar" were all a) large and b) the same/very similar. Python's answers to that problem are much less good:

1. Duplicate them and write a unit test that ensures that the __annotations__ property of the functions that should have the same signatures remain in sync (or sub/supersets of each other, or whatever you'd prefer). This addresses drift, but requires a testing system and doesn't save the duplicate code.

2. A hack: use object constructors for functions. Write the in-common parts of the signature in the constructor of a parent class, and then put the body of "foo" and "bar" in the __init__ methods of child classes whose instances are not used/are useless, taking advantage of the superclass relationship to indicate to typecheckers that the parameters are all shared. For example, many typecheckers will do the right thing when handed code like this:

    class _Super:
        def __init__(self, arg1: SomeType, arg2: SomeOtherType, ...):
            self.arg1, self.arg2 = arg1, arg2
            
            
    class foo(_Super):  # Bizarre casing is intentional, this is not meant to be used as an instance
        def __init__(self, *args, **kwargs):
             super(*args, **kwargs)
             ...  # Business logic of old "foo()" method goes here, using object fields self.arg1 etc. instead of named variables.
             
    class bar(_Super):  # Bizarre casing is intentional, this is not meant to be used as an instance
        def __init__(self, *args, **kwargs):
             super(*args, **kwargs)
             ...  # Business logic of old "bar()" method goes here, using object fields self.arg1 etc. instead of named variables.
             
    class wrapper(_Super):
        def __init__(self, *args, **kwargs):
            foo(*args, **kwargs)
            bar(*args, **kwargs)
This does solve the duplication problem, and you can use dataclasses with __post_init__ methods for your "foo"/"bar" business logic to smooth out some of the boilerplate and weirdness there, but it remains a very bizarre coding style which substantially trades away readability/familiarity (and performance, if this is on a very hot path) in return for type-checker-friendliness.

3. Use a databag object (ideally an immutable slotted class, dataclass, typing.NamedTuple, [c]attrs, or something of that sort) to encompass all the data that would previously go in your argument signature, so that "wrapper", "foo", and "bar" all end up taking a single such object as their sole argument and accessing fields of that argument to do their work. This is probably (maybe? Lots of Scotsmen in this area...) the most traditionally Pythonic of these options, but is still a far cry from the convenience of something like functools.wraps.

Post reply on HN