"Because Animal is a Protocol, any class that defines feed() becomes an Animal"
is slightly incorrect.
By default, protocols are only relevant for static type analysis, but you _can_ enable runtime instance checking using the @runtime_checkable decorator.
(So you can do "if isinstance(foo, Animal):...", but AFAIK this decorator comes with a performance penalty)
Protocols allow you to explicitly declare a class implements them by making them a base of the class, but do not require it. Every class that implements the specification of the protocol is usable where the protocol is specified, not only those explicitly marked with it. Python didn't make c#'s doofy mistake of requiring the programmer to explicitly annotate everything. So, not every implementer of the protocol will have isinstance be true.
That is correct, but the @runtime_checkable decorator (which goes on the class that defines the protocol, e.g. "Animal" in the blog's example) makes it so that any class that implements the required methods (e.g. Rabbit) also passes the isinstance test (so isinstance(Rabbit, Animal) does actually return True, even though Rabbit is not defined as a subclass of Animal, i.e. NOT class Rabbit(Animal) !)
Whether it makes sense to use that is a different question, but it is possible :)
it looks rather gross to me, and more dangerous than useful. why bother with a half check that can only see if the methods/attributes are present, but that can't guarantee they do what you need? better to not offer such a check at all, I should think.
but I'm not a fan of a great number of things python has been doing over the last some years.
if you were going to do such a thing (perhaps to allow type-safe callbacks from untyped code), I would think a wrapper would be the proper method, that checks the incoming parameters and the outgoing return value, raising an error if those are of the wrong sorts, and possibly further wrapping if something was declared to fit a protocol.
I can program in a dozen languages or so, so I'm not coming at it from some place of knowing python and complaining about C#. There's plenty to complain about in python's typing.
Having to hand annotate classes to indicate interface membership instead of just writing classes and letting the compiler perform structural analysis depending on their usage is annoying.
What happens when member names collide and you match an interface you shouldn’t have? It’s an issue even in Go, which, unlike Python, has a usable type system.
I suppose if you were so unfortunate as to have two interfaces name the same function with different expectations on what it does, you'd end up having to use an intermediate object to wrap around your base one, to ensure it matches the required interface, possibly adding a helper to wrap the current object for convenience at callsites.
In C#, you would instead define a method and specify the name of the interface in order to override the method it receives to be different from the base method that is experiencing the conflict.
I know this is a real issue, but I don't think this is excessively common for either language.
And the real answer for both would be to rename/prefix the names in the interfaces so that they aren't conflicting in the first place, if you have the ability to do so :-)
There are certainly always trade offs when building something.
As a (former) NetEng, it bothers me to no end that so many people claim "it's the network" when their application is slow / broken, without understanding the actual problem.
I often use generator expressions for the intermediate values (so I don't allocate a new list for each step), but I find this to be much more readable.
I assume you're basically referring to this quote from the article?
"Ignore fields coming from the API if you don’t need them. Keep only those that you use."
IMO this addresses only one part of the problem, namely "sanitize your inputs".
But if you follow this, and therefore end up with a dict whose keys are known and always the same, using something "struct-like" (dataclasses, attrs, pydantic, ...) is just SO much more ergonomic :)
> This is a lie. A "session through the NAT" does not really expose the host to the outside world, because in 99% of the cases this is a TCP session, and the NAT machine would drop all "out of order" packets.
No, it's not. NAT only translates addresses and does not inspect the TCP "internals" (like sequence number etc, which would allow it to block certain packets).
What you are describing is a stateful firewall that allows "reply packets" for an established TCP-session.
>No, it's not. NAT only translates addresses and does not inspect the TCP "internals" (like sequence number etc, which would allow it to block certain packets).
Yes it is. How would it forward response packets back if it doesn't track connections?
In real life I haven't seen "stateless NAT" for about 20 years.
But cgnat machines usually go beyond that and even verify sequence numbers.
Also this:
"Because Animal is a Protocol, any class that defines feed() becomes an Animal"
is slightly incorrect.
By default, protocols are only relevant for static type analysis, but you _can_ enable runtime instance checking using the @runtime_checkable decorator.
(So you can do "if isinstance(foo, Animal):...", but AFAIK this decorator comes with a performance penalty)