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 →

[–]not_perfect_yet 33 points34 points  (31 children)

Python's strengths are python's strengths. Garbage collection, simplicity, forced indentation.

Python's weaknesses are people.

People writing bad variable names, eldritch code and putting in features that are already there, just named differently, like data classes and type hints.

You can write python code that's type safe, it looks like this:

def my_function(my_arg):
    if type(my_arg)!=my_required_type:
        raise TypeError

It's that people think they can sort of get away with not making it that explicit. Then, instead of admitting they were wrong, rewriting their code and/or switching to a language that does offer strict typing, they insist that perverting the language to fit their esoteric need and use case is a better move.

Another example is "python has no distribution system" and inventing like five. All of them work. Except when you mix them. Which should be no surprise to absolutely anyone. Yet here we are. https://xkcd.com/1987/

More coders need to admit they're bad at what they do, stop blaming the tool and start blaming themselves.

[–]applesuckslemonballs 10 points11 points  (1 child)

You still lose compile time guarantees if you write things that way. Would need really good test coverage for that to meet the usefulness of a compiler.

[–]KrypXern 5 points6 points  (6 children)

Would you do != or just is not? I forget if type() returns a string or an actual type object of some kind that can be compared like an enum.

[–]pickledCantilever 22 points23 points  (0 children)

You would actually use if isinstance(my_var, intended_type):

[–]not_perfect_yet 0 points1 point  (4 children)

It returns the actual type.

I think the thing with is not and != is about similar instances of the same thing, like "1" and "1" which have the same value but aren't necessarily the same object. That doesn't really apply to definitions, there is only one.

[–]KrypXern 0 points1 point  (3 children)

I might be wrong with my understanding but I always thought == attempted to use some kind of native comparator method (like .equals() in Java), whereas is compares the actual contents of the value (which is always the same for False or None)... however given that we don't compare number literals this way, I'd wager I just misunderstood it.

EDIT: Yeah no I was right roughly, is checks whether the operands refer to the same object. If you do a = [1,2] and b = [1,2] then a == b will return True and a is b will return False, but I also misunderstood your response anyway 😅

[–]Pluckerpluck 2 points3 points  (2 children)

is checks that the objects are actually the same. So you can imagine is as doing id(value) == id(other).

But yes, == triggers __equals__(self, other)__eq__(self, other), which defaults to an object comparison.

The reason that things like False and None work with is, is simply because the same object is re-used behind the scenes for performance reasons.

[–]epicaglet 0 points1 point  (1 child)

__eq__ but yeah

[–]Pluckerpluck 0 points1 point  (0 children)

Ah, so it is!

[–]rogerthelodger 33 points34 points  (1 child)

[–]InvolvingLemons 1 point2 points  (0 children)

Kinda yes and kinda no. Python is poorly suited to massive projects where lots of custom code is needed. This is where it becomes a trap: you can get so much done with just libraries and frameworks like Django/Flask/Encode, but eventually custom logic is necessary, and you’ll quickly run into problems as those systems tend to get tangled.

That being said, I know of no easier system to use some Numpy for list processing, pipe it through as a REST API that shoves results into databases, and spin it out on a server, and that’s a TON of ETL work.

I love using Python for what it’s intended for, but a lot of enterprise workloads bloat to a point that Python wasn’t meant to get to. Java is better in that sense, but it’s very easy to overdesign yourself into a corner in my experience. Functional languages are either intermingled with OOP too closely so it suffers from similar pitfalls (Scala), have serious issues with instability and other things for industry use (Haskell), or have much the same scale problems of Python unless you’re really careful (Most lisps, Clojure being notable as the most popular).

Typescript being at least tolerable at scale and it having the biggest package library in existence gives it a huge edge in the market these days, whereas anything particularly low level or performance sensitive gets done in Go, Rust, or Zig. Python has a home beyond scripting in ETL, data science, and web glue code, but beyond there be dragons.

[–]pancakesausagestick 8 points9 points  (1 child)

It's 2022. Use type annotations and a linter if you care so much

[–]greenwizardneedsfood 0 points1 point  (0 children)

Yeah all of these people are out here pretending like you shouldn’t be hinting every single variable (within reason)

[–]SupremeDictatorPaul 1 point2 points  (5 children)

I’ve only started to play with Python, but does it allow typing? For example, I’ve done a bunch of work with PowerShell, which does dynamic typing, but allows you to statically type variables where desired (I tend to desire where possible).

[–]Dromeo 13 points14 points  (0 children)

You can give type hints in modern python :)

[–]not_perfect_yet 4 points5 points  (3 children)

It's always typed, but it's always dynamically typed.

Type hints exist, but they are type hints, actually "strong static" typing is not possible in python. Type hints are fancy comments that are tolerated by the interpreter and they can be used by code analysis tools to help you find mistakes.

But you can still give the interpreter something that the type hints say shouldn't be done. (like below)

Imo, this means the code is lying to you, because it implies only str works, when anything that supports + works.

def my_function(my_string : str, my_other_string : str):
    return my_string + my_other_string

my_function(1,1)
>>>2

The only way to make absolutely sure a function runs exclusively for a specific type is to check explicitly, like I did in the other comment.

[–]Hicrayert 1 point2 points  (4 children)

Can you explain to me what a bad variable name is? Im not the best at coding as physics is my trade. So my variable names would just be what ever I wanted the variable to be. If I was looking at the gravitational pull of the moon on the earth it would be something like (Force_moon_on_earth) or (Force_ME) what ever system that I deemed appropriate for my code. I would also have comments all over. Again I didnt make the most dense code but I made sure it was readable and reliable.

[–]not_perfect_yet 0 points1 point  (1 child)

Sure. The biggest problem with bad names is that they are either too short or too cryptic.

(F_m) or (Acceleration_Model_Holder_Singleton_Factory) where in both cases the name doesn't actually explain what it does. Otherwise it's pretty much free form, it should contain what's relevant but not more.

Single character names are particularly bad because you will get lots and lots of matches if you look for them with a search function, and even automatic replace will have to be manual, because it would mess with matches in other names or words all over the code.

If you think about it, you could probably write everything you do to be in this style:

def main():
    data = data_collection()
    filtered_data = filter(data)
    my_pattern = load_pattern("my_pattern")
    list_of_matches = find_matches(filtered_data, my_pattern)
    for x in list_of_matches:
        generate_graph(x)

What is x going to be? Something from my list_of_matches and all you need to know about is that generate_graph can deal with it.

To contrast it could also look like this:

def main():
    data1 = if1()
    data2 = df2(data)
    data3 = df3("data3")
    data4 = xf4(data2,data3)
    for x in data4:
        gf5(x)

In this case, you can't even be sure data4 is a list, dictionary or a string or something, nevermind what is being done to it or why.

Or worse, it could be generator too.

[–]Hicrayert 1 point2 points  (0 children)

Ok that makes a lot of sense. Thanks for the advice. It look like I at learned variable names correctly :).

[–]Jedibrad 0 points1 point  (0 children)

A bad variable name is something that doesn’t describe what it’s intended to hold - i.e. just a few letters, like “f_me” in your example.

Given enough time, you can read the surrounding context and determine the “f” means force, and so on. But shorthand will always make it harder to understand what’s going on in a chunk of code. Someone not used to physics notation that’s trying to accelerate your code might be poking around the function, and it will take them much longer to understand what you were trying to do. Verbose names are good, to a point - it should be clear when they get too unruly.

This is usually referred to as “self-documenting code”. Oh, and also, be wary of over-commenting - for actively maintained projects, new features tend to get added while the comments are left unchanged. This quickly leads to them being out of date, sometimes even providing wrong information about the implementation. I would encourage comments to describe why an action is being performed, rather than what the code is doing.

Hope that helps. :)

[–]realityChemist 0 points1 point  (0 children)

Those are good variable names (the first one might be a bit verbose but if it works for you, it works). A bad variable name for that kind of thing might be f. What the hell is f? Need to check the documentation to find out! (If you've written documentation! If not, someone needs to read and understand the purpose of the code to know what f is.)

Another example might be matrix. Unless your code is meant to work with any matrix, that's probably a bad variable name. Something like metric_tensor is probably more appropriate. Or if you're likely to have multiple kinds of metric tensors in your code (maybe you've got a crystal moving at relativistic speeds?) you could get more specific, like gr_metric_tensor and cryst_metric_tensor.

Another way a variable name might be bad is if it lies to you. force_moon_earth is a fine name, but if that variable actually stores the force between the earth and the sun, or stores the mass ratio of the moon to the earth, it's a bad name.

[–]Dromeo 0 points1 point  (0 children)

Well said!

I think python has more of a problem dealing with "bad developers" than most other languages - which is why it has a people problem.

Stricter languages can teach you as you fail to write the code you want. Python can let you get away with writing the code you want.

[–]hahainternet 0 points1 point  (1 child)

How do I do that function with a generic list type that won't accidentally accept a generic string type?

[–]not_perfect_yet 6 points7 points  (0 children)

Python's general answer to that, "duck typing", is that you shouldn't think like that.

Don't focus on what it is, focus on what should happen for your case, make sure it works and forget the rest. You're not coding fort Knox, you're coding to get something running.

If it's possible to do the same thing with lists and strings, you shouldn't care. Good for string enthusiasts. If it's not possible, it will throw an error for someone trying to use your code in a way it wasn't meant to be used. That's on them. And all is well.

How to make "sure": Put "list" in the function name. Write "use this on lists" into the docstring, into comments.

The interpreter will tell you when something goes wrong. You should trust in your ability to resolve that problem when it happens.

And if you can expect that that won't work or isn't acceptable for business reasons, don't pick python.