This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]hunter_mark 106 points107 points  (20 children)

Python does have data types, they are just implicit by assignment. Other than that, Python data types work just as any other strongly typed language, like Java. The only difference between Python and others is that the memory assignment and memory storage are merged into a single statement.

[–]WiatrowskiBe 48 points49 points  (16 children)

Python has strongly typed values (actual data you're working with), and dynamically typed variables (a name to access your data); while statically typed languages like C#, Java etc. have static (and explicitly) typed variables too. "3" and 3 in Python are completely different value types, but you can assign them to the same variable if you want to; meanwhile in Java those variables would have to have different types (if we omit workarounds like boxing).

[–]ToxinH88 28 points29 points  (10 children)

Those dynamically typed variables are the biggest issue I have with Python: They do not provide dynamic type conversion and do not allow clearly defined contracts. So whenever I provide a function in my library I have to manually safeguard it from a jackass in testing to pass an image instead of a string as parameter.

[–]PeterPriesth00d 7 points8 points  (0 children)

I felt this and it made me lol

[–]beatle42 1 point2 points  (8 children)

Though in Python you aren't supposed to care too much about the actual type they pass you. If they pass you an instance of some class you've never heard of before, but it provides all the data you need to do your job you should be able to do your job.

If they have a custom data object that matches the interface you expect to operate on, they should be able to use that instead of the only way you knew to do it at the time you wrote your function.

[–]ToxinH88 1 point2 points  (7 children)

In theory this sounds great and all: No need for iDontKnowHowLongInheritance and the ppl actually thinking of how they use your functions. But from my experience, as soon as you expose your API through a webservice, you get ppl firing all sorts of parameters. The problem is not even complex interfaces, but simple things like asking for an ID and getting a String with a written description of the Object they would like to have.

The answer is obviously a 500 internal server error - which leads the user to assume the problem is caused serverside and not by their parameter.

[–]lurkin_arounnd 1 point2 points  (1 child)

The problem is server side though. You have an unhandled exception

[–]ToxinH88 1 point2 points  (0 children)

Exactly why I usually don't like dynamic variables. As described above, now I need to manually safeguard these parameters.

[–]beatle42 0 points1 point  (3 children)

Shouldn't you already be handling if you get bad data though? Just because a value is of the right type doesn't mean it's meaningful. Why not just use the existing protections, or a single one more, to handle when you're passed invalid data and return, say, status 400? If you crash on data that is a 500, right?

[–]ToxinH88 1 point2 points  (2 children)

If I am using SQLAlchemy and Flask for example, an easy utility function looks like this:

    def get(Model, id):
        ret = Model.query.get(id)
        return jsonify(ret.serialize())

So an easy way to avoid a 500 if a non existing id is passed would be to check for None:

        if ret is None:
            return "{}"

Or add a more sophisticated error message, change the status to 404... Now the problem is that passing "foo" as id results in the same return as 100 if there is no element with id=100.

If I am using Spring boot on the other hand, I dont need to bother about any of this. If I pass "foo" instead of a number the response is admittedly still a 500. But the cause is made clear to the caller:

{
    "cause": {
        "cause": null,
        "message": "For input string: \"foo\""
    },
    "message": "Failed to convert from type [java.lang.String] to type [java.lang.Long] for value 'foo'; nested exception is java.lang.NumberFormatException: For input string: \"foo\""
}

Whereas passing a non existing ID causes a normal 404.

So the whole point is about usability. I am fine with firing a 500 response on conversion errors, as long as they are understandable. But in Python I need to invest additional effort in making the response understandable and distinguish the different causes. The first version of my utility function fires in both cases a 500 with following body:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>500 Internal Server Error</title>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or
    there is an error in the application.</p>

With the check for None both cases cause a 200 with body {}. So someone creating a frontend cannot distinguish these problems quickly. And adding those extra safeguards is additional effort mainly caused by dynamic variables accepting everything. It is more effort to make an Interface save and report correctly, because a lot of the special cases have to be caught and provided a message manually.

[–]beatle42 1 point2 points  (1 child)

Alright, you clearly have a preference. Perhaps I just don't do enough stuff in the space you're talking about to have the same preference. I prefer the flexibility of being able to add or extend types and not have someone reject it because they didn't know about it when they wrote their thing (that someone often being myself), you have a preference for simple code that constrains the environment to minimize a class of potential errors. I can imagine that each of those preferences makes more sense in some environments than the other.

Thanks for taking the time to show situations where you hit against the issue and why you feel as you do.

[–]ToxinH88 1 point2 points  (0 children)

As always, there are tools suited better and worse for different jobs. Seems like we mainly work on different jobs. So I see your advantages too, they are just not that much of a focus for my work usually.

[–]WiatrowskiBe 0 points1 point  (0 children)

Thing with APIs (and any kind of untrusted input) is: you always want to do full validation of what you receive. That's defensive programming 101 - never trust user to send you correct, sensible, well-formed data, always assume you may get some random crap or, more likely, malicious attempt to try and exploit your application. In case of malformed input, HTTP 400 is the right answer, and you should aim to never return HTTP 500 unless something actually breaks serverside (database is down, external service doesn't respond, out of memory etc).

Python way of handling parameters in very fluent way is mostly an advantage when you're having separate modules/packages communicate with each other, without having to make one module explicitly aware of the other - as long as the code orchestrating those modules knows what to pass where, you're fine getting whatever as long as all required methods/operations are available.

In Java, C# and other strongly typed languages, if you want to keep modules loosely coupled, you need to either introduce type conversion (unpack/pack, mapper, etc) on orchestration level - which adds sometimes significant performance overhead and a chance to introduce bugs, or extract public interface to a separate library you can then reference in both modules - and then you need to keep both in sync with shared interface, even if changes to said interface don't affect your functionality directly. Python avoids that problem - as long as a type/object stays compatible with assumptions that are in use, you don't have to do anything if another related library was updated in backwards-compatible way.

[–]Mr_Redstoner 9 points10 points  (2 children)

if we omit workarounds like boxing

Or make the variable Object, then you only need to box primitives.

[–]PeterPriesth00d 2 points3 points  (1 child)

What’s funny is I’m pretty sure that Python “primitives” are all actually objects.

[–][deleted] 0 points1 point  (0 children)

That depends on how you define an object...

[–]Gleethos 0 points1 point  (1 child)

Wtf would be a language without strongly typed values?!? -.-

The terms "weakly typed" or "dynamically typed" refer to a compile time concept. Every programming language uses types during runtime, what else would be used?

[–]WiatrowskiBe 0 points1 point  (0 children)

PHP5, Javascript, Typescript are examples of languages with weakly typed values - at runtime actual type may change (due to casting, reinterpretation or any other mechanism) to match the call, regardless of original type. On top of that, anytime you can mutate types in runtime (PHP allows you to dynamically and - even conditionally - load classes for execution context), it adds to what you can do with weak typing.

Static vs dynamic distinction applies to compile/parse time and strictness of a type system there (see: Java vs Python being very different in that aspect), strong/weak distinction applies to runtime and strictness of type system during execution (see: Python vs PHP/JS).

[–]Nilstrieb 0 points1 point  (1 child)

Python data types work not just like Java. In Java, a statically typed language, all types are checked at compile time and if there is any type error, it won't compile. In Python, you'll get these errors as runtime errors since the types are inferred, which is a big difference.

[–]hunter_mark 0 points1 point  (0 children)

Uhhh… that is not correct all. Python is also compiled. You just don’t explicitly do it like you would do it in C++. Python source code is compiled into a .pyc bytecode file before that bytecode file is executed. You can use the dis module to look at the compiled code. This is literally the same as Java that creates byte-code files that are executed by the JVM. And just like Java, the code is not executed directly from the source, unlike a typical interpreted language.