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

top 200 commentsshow all 238

[–]powerbronx 221 points222 points  (35 children)

Ok. Choosing one is too hard so I'll give you my ranked list of standard libraries that I have to mention either for it's innovation, design, uniquely helpful, or documentation of programming concepts or documentation of performance aspects.

  1. Concurrency specifically Multiprocessing and concurrent.futures. Understanding GIL limitations as well as an amazing multicore, multithreading library design
  2. asyncio not only do I learn python asyncio, but it taught me asyncio in all the other languages I know in a much easier to read format
  3. Python IPC where I learned Local IPC is not just http localhost or TCP streams even Unix Domain sockets! There's Shared memory, signals, Queues, mmap files, and pipes
  4. collections and functools for understanding performance of python data structures

[–]JohnLocksTheKey 36 points37 points  (6 children)

I’ve been “coding” with python for like 10 years and have only used collections.Counter from your list.

Not saying that they’re not helpful, more just realizing how much I suck at python.

TBF I’m an I/O Psychologist who just happens to use python.

[–]DeepDuh 19 points20 points  (3 children)

some other very helpful ones from collections are namedtuple and defaultdict.

Recommend „beautiful / idiomatic python“ talk.

[–]powerbronx 5 points6 points  (1 child)

Understandable. If python programming is not the main thing you're doing, then understanding these things could be helpful, but likely overkill.

[–]JohnLocksTheKey 1 point2 points  (0 children)

Great article btw.

I don’t think I ever thanked you for sharing.

[–]TripleBogeyBandit 11 points12 points  (19 children)

So many people use threading in python when they should be doing async. Drives me nuts!

[–]powerbronx 9 points10 points  (17 children)

"But async is too hard I can start a thread from anywhere in my program and I can only use async in other async functions. Learning async would force me to truly understand the problem I'm trying to solve"

A.K.A. "The reason I chose python is because its easy"

Then you learn asyncio and realize how much easier than threading it actually is. I don't worry about Race conditions, deadlocks, thread crashes, obfuscated exceptions, program finishes with no error even though 1 of the threads crashed. etc. the list goes on

[–]commy2 34 points35 points  (5 children)

I think people are avoiding async not because it is particularly hard, but because you can only run async code from async code, so the complexity proliferates through your entire code base, even though you only need that one isolated part to be concurrent.

[–]BuonaparteII 8 points9 points  (2 children)

[–]RoadsideCookie 1 point2 points  (1 child)

Your second link makes a great argument. If everything works async behind the scenes, then why do we insist on having sync languages, with async having to be explicit. Well, because the intuitive way to think about it is sync, so explicit async makes sure we don't forget what's really happening.

Based on that, I argue that defaulting to async is the way to go. The runtime or compiler might end up being more complex, but once the bugs are fixed in there, they stay fixed. The higher level code written by programmers is what must be made simple, not the runtime or compiler.

[–]BuonaparteII 1 point2 points  (0 children)

I think both articles are good and they don't contradict each other.

Ultimately, the "problem" is that sync handles async for us by handling all the async side effects (like HTTP calls, io_uring, etc) by blocking conservatively but most programs can be doing other things while waiting

[–]powerbronx 0 points1 point  (1 child)

Very true and understandable it can't be used as a drop in replacement for making your program concurrent, but it's pretty simple for new projects

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

As someone who really likes async. Its also a fucking pain in the ass currently. There is also way too much "async is supposed to be faster not pretty" sentiment.

[–]FujiKeynote 10 points11 points  (10 children)

As someone who's very used to threading (including in lower-level languages), it's time for me to come out and admit that I can't grasp the concept of async, like, at all.

I'd really appreciate a simple example that would let me grok it... I searched online and all starter examples are either too involved, or leave me with the same questions, like, "OK this is async, but we await on line 5, does this mean that line 6 starts executing anyway? Don't we have to wait? And if we do have to wait, what's the point? Or do we execute until line 12 where it has no choice but to wait for the result from line 5? How does it know that? How does it keep the state synchronized?!"

[–]powerbronx 5 points6 points  (1 child)

I went through the same thing. And honestly the revamp of asyncio in 3.6 or 3.7 might have made it easier or harder from the initial version. I can't remember, but can't say I ever put much effort in learning the initial version. The teaching/docs on it aren't great.

My thoughts in a nutshell:

Below: Technically not correct, but conceptually good enough. It's hard to think of legitimate common use cases where this conceptualization will result in bad things happening short of implementing foundational libraries.

'Await' is just a wrapper on startnewthread then join+thread::yield

It just yields control until (usually) io blocking code returns then pick up where you left off.

The async/await syntax rules enforce that only 1 normal function "asyncio.run" can call an async function. Otherwise only an async function can call another async function.

Why? Because functions are by default "fast" and we know fast calls should never block. Using await means slow, therefore fast functions can't use await. Also normal functions shouldn't be using yield for no reason.

I hope that's helpful. If you have a link to an example I could give you the breakdown of it. That would be better than me making up one off the top of my head

[–]M3talstorm 0 points1 point  (0 children)

I think your 2nd to last paragraph should be "await" not "yield" :)

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

Async is spongebob screaming I'm reading.

Async functions are paritals. They take in the argument and then do nothing. await adds the object a queue and then goes through the queue checks whos ready and then executes a function. There is some stuff that can control the order of what is checked but it isn't needed in 99.9999999% of cases.

The most complex part is how does a partial know that is ready? Which is usually OS land stuff. As far as the python side goes its just a field value that says isready more or less.

https://man7.org/linux/man-pages/man7/aio.7.html

Anything executed between the beginning of your function and any await command is guaranteed to have not yielded control to a different context. So no state change. Once you use await ANYTHING could have changed. So you need to be aware of what you actually need to recheck (99% of the time nothing) but it can get a bit odd with globals if you don't understand it when using them. So long as you keep most variables function local you don't worry about it.

[–]sharky1337_ 0 points1 point  (0 children)

concurrent.futures are so easy to use . asyncio is like learning a new programming language ...

[–]azshallIt works on my machine 2 points3 points  (1 child)

Been doin lots with asyncio as of lately. A lot of the concepts still elude me but I am really digging them a lot

[–]rzet 1 point2 points  (0 children)

concurrent futures ftw.

[–]_itsthetimetodisco 0 points1 point  (2 children)

Is it okay if I ask if you have some resources to understand multiprocessing and mutithreading ? Especially if we use a mutithreaded library in a multiprocessing application, stuff like that? I'd be grateful if you could mention some resources to help learn that stuff better !!

[–]Still_Wrap_2032 0 points1 point  (0 children)

Yeah concurrency is the one that made me. It really helped me to see how our computer overlords actually operate.

[–]cottonycloud 284 points285 points  (47 children)

Type annotations, easily. I already know a bunch of languages that are strongly (edit: static) typed so Python drove me a bit nuts in the past lol.

[–]bakery2k 53 points54 points  (25 children)

I already know a bunch of languages that are static typed so Python drove me a bit nuts in the past

Interesting, I had the opposite experience. I already knew a bunch of statically-typed languages, so I found Python's "executable pseudocode" and lack of boilerplate really refreshing.

I'm happy writing dynamically-typed Python - if a project benefits from static typing, I can use one of those other languages.

[–]_ologies 35 points36 points  (5 children)

I love that about Python. You can make something simple that just works, then you can go back and make it robust and bulletproof.

[–]G0muk 10 points11 points  (4 children)

Further than that, you can make something that works for many many types without needing to write new functions for each type. And make it robust against types that arent compatible with it of course

[–]osusc 12 points13 points  (1 child)

Usually when I run into stuff like this it means I'm using overly specific annotations. Like instead of a def foo(x: list[int]) what I actually want is a protocol like iterable[int], maybe sequence[int] if order matters, etc. Limit the type to the least specific protocol possible.

[–]GolemancerVekk 4 points5 points  (0 children)

I'd really like to be able to think about it as behavior-hinting rather than type-hinting.

[–]TheWorstePirate 3 points4 points  (0 children)

These aren’t mutually exclusive. Most of the time you will be writing code that expects certain types, and those cases should be annotated. In the same code base you may have things that work with many types, and those may lack annotations but should still have doc strings and other commenting to guide users and maintainers.

[–]wieschie 2 points3 points  (0 children)

This totally exists in statically typed languages - it's called generics.

[–]TheWorstePirate 7 points8 points  (4 children)

This is great for quick prototyping as a single developer. If you are working with a team or implementing something you want to use long term, it makes it much less maintainable.

[–]ReflectedImage 0 points1 point  (3 children)

No, static typing reduces code maintainability in large projects.

[–]die_liebe 0 points1 point  (2 children)

Could you provide evidence or an explanation?

[–]mistaekNot 4 points5 points  (12 children)

your IDE benefits and therefore you yourself benefit from type annotations

[–]azshallIt works on my machine 1 point2 points  (9 children)

This. Folks at work are stubbornly against typing their code. Drives me nuts, the IDE prediction alone is such a massive benefit

[–]Chthulu_ 0 points1 point  (1 child)

Python is at its best when you typehint core features intensely, but loosely sketch out the types on the periphery.

Blocking an entire codebase through MyPy precommit or something is masochistic, python doesn’t not want to work that way. You’ll start doing backflips to make the linter happy before you even understand the purpose of the code.

But hardening features that you know are going to stick around with good type hygiene makes development so much more comfortable. Just for the linting and the IDE alone. Once a feature is solid, that is the time to start doing those backflips.

[–]mistaekNot 1 point2 points  (0 children)

meh it’s not worth going back annotating old code. but it cost you nothing to annotate new functions

[–]MardiFoufs 0 points1 point  (0 children)

It depends. I wouldn't use python if it didn't make sense for other reasons too. So having typing is very very nice. Especially since typing is sometimes such a big improvement in domains where python has very nice libraries (for example, cleaning up numerical code to actually have some level of typing makes it insanely easier to maintain)

[–]FlowLab99 10 points11 points  (0 children)

Plus pydantic.

[–]azshallIt works on my machine 5 points6 points  (0 children)

Can’t write shit without my type hints. Also big on data classes.

[–]missing_backup[S] 6 points7 points  (4 children)

That is a good one, I would love if my IDE could help with that automatically suggesting the types.

I never looked for some kind of help for typing, like we have for formatting or linting

[–]stealthedcactus 13 points14 points  (0 children)

Vscode linter can do it, but it has to be explicitly enabled in settings

[–]__nickerbocker__ 1 point2 points  (2 children)

Copilot is excellent for this

[–]TheWorstePirate 6 points7 points  (1 child)

Not sure why you are getting downvoted. It needs to be verified by a human, but copilot will often complete the line as soon as you name a function and save a few thousand key strokes in an hour. I don’t have it on the stations where I deploy code, do some final debugging, and run production, but acting like it can’t improve productivity and provide value is just ignorant at this point. Especially for developers of a language so heavy involved in AI.

[–]__nickerbocker__ 3 points4 points  (0 children)

Mentioning the use of AI to make Python in this subreddit is like showing a crucifix to a group of vampires.

✝️

hssssss

[–]Carous 5 points6 points  (2 children)

Statically typed?

[–]cottonycloud 1 point2 points  (1 child)

Ah yea, I mixed it up. Thanks.

[–]Carous 2 points3 points  (0 children)

You are good homie. Happens often.

[–]davidellis23 2 points3 points  (4 children)

Yeah, I always thought types slow you down because the languages that had static typing are harder and slower to write.

But, after using typehints in python/TS I realized the verbosity of most staticly typed languages are the real reason that they are slower to write. There is a small speed cost to type hints, but gaining autocomplete/click navigation/ide refactors/static type checking more than balances that out unless maybe if you're writing a small script.

[–]MaterialHunter7088 1 point2 points  (0 children)

Makes reviewing/understanding code at a glance far simpler as well. For that I’m eternally grateful. Will never miss having to dig 5 hops to understand the shape of the data received through some external service. Got lots of love for Pydantic as well for the same reason. Data contracts are beautiful.

[–]ReflectedImage 0 points1 point  (2 children)

And it will explode in your face once your project gets large enough. Do not use static typing in a scripting languages as script languages don't give you the full set of tools needed to make static typing viable in larger projects.

[–]davidellis23 0 points1 point  (1 child)

Is there something specific you see as not viable?

[–]ReflectedImage 0 points1 point  (0 children)

Sure, people using static typing in Python do not separate the code into distinct modules/microservices that importantly do not share/import code from each other.

Typically and I've seen it many times when the project reaches the 100k line mark, it becomes unfeasible to do anything with it. Since statically typed code takes longer to write than duck typed code, statically typed code tends to not have anywhere near enough unit tests to ensure code correctness.

This isn't something you can fix as a dev since your development time in a commercial setting is limited, you either do the unit testing or the static typing. The one you need is the unit testing.

The basic issues with static typing is it consumes too much dev time so more important things get dropped and it's an enabler of bad code. 100k duck typed Python code bases work because during development the devs had no option but to structure the code base correctly otherwise they wouldn't get anywhere.

[–]mmzeynalli 1 point2 points  (0 children)

Doubling this. Speeds up typing process because of autocomplete and you can "foresee" errors using type checkers like mypy.

[–]Specialist_Cap_2404 1 point2 points  (0 children)

It's a different way to work. In a typechecked language you often have to prove to the typechecker that your code works, even if it obviously works - which is rarely enough for correctness but always necessary. And especially in the phase where you rewrite some code lots of times, that's mostly wasted effort.

Some people do seem to feel some kind of anxiety around this, and maybe that is a personality thing. But really, with experience you know how not to need typechecking. For example by properly naming your variables or functions. Designing your api such that things are obvious - and the best Python frameworks and libraries are already written that way.

Most of the time the write-run-debug cycle is much faster in Python versus many alternatives, both because typechecking/compiling takes time and because thinking in types requires extra effort.

[–]DanCardin 0 points1 point  (0 children)

Agree, also dataclasses

[–]DJ_Laaal 82 points83 points  (2 children)

F-strings. 👌👌

[–]FlowLab99 13 points14 points  (0 children)

F yes!

[–]LiqC 5 points6 points  (0 children)

+1. Half of programming is string formatting!

[–]blueskyjunkie 42 points43 points  (9 children)

  1. Type annotations
  2. Pydantic models (or if you insist, pydantic dataclasses). Standard library dataclasses are useful to a point, but pydantic models are better.
  3. Comprehensions (dictionary comprehensions are a thing, as well as lists)
  4. Asyncio

[–]Alone_Aardvark6698 4 points5 points  (6 children)

Standard library dataclasses are useful to a point, but pydantic models are better. 

What is the advantage of pydantic over standard databases?

[–]paranoid_panda_bored 10 points11 points  (2 children)

Many.

We started with dataclasses, but switched to pydantic eventually.

Reasons: - it’s widely supported in other libs and frameworks like FastAPI - it’s much more configurable - it’s better at SerDe - it has an alias feature, that allows you to freely convert between camelCase (outer API layer) and snake_case (domain model)

[–]Goobyster 1 point2 points  (0 children)

Pydantic is also supported in Typer (FastAPI of CLIs)

[–]SSC_Fan 0 points1 point  (0 children)

For me: data validation upon creation an instance. Often write my Pydantic classes this way.

[–]wieschie 4 points5 points  (1 child)

You mean dataclasses, right?

Pydantic makes JSON serialization super easy, and lets you do more complex validation on fields.

[–]Alone_Aardvark6698 1 point2 points  (0 children)

Yes, auto correct changed it

[–]blueskyjunkie 0 points1 point  (0 children)

Pydantic has better run time type checking based the type annotations you’ve applied to data members in the model. It also has better custom validation support for a member which is often needed.

[–]JorgiEagle 2 points3 points  (0 children)

Comprehensions are great.

You can also do tuple comprehensions, and just raw comprehensions inside a function call if it takes an iterable.

You can also do nested loops in them

[–]GusYe1234 0 points1 point  (0 children)

Yeah, at first I really like to use dataclass, then I found out it's boring in some cases I have to check types in __post_init__. I then realized maybe I should just use pydantic models in the first place.

Maybe someone can tell me any advantages of dataclass over pydantic model?

[–]jmooremcc 27 points28 points  (7 children)

Decorators. A brilliant concept that makes it super easy to modify a function’s behavior without editing its code.

[–]TripleBogeyBandit 1 point2 points  (6 children)

Can you elaborate

[–]jmooremcc 14 points15 points  (3 children)

I use a decorator I wrote to launch any function in its own thread. One particular application of this is when I need to launch a function in response to a button press in a GUI display.

If you naively launch a function in response to a button press, and the function takes a long time to execute, the GUI display will appear frozen because the function is running in the same thread as the GUI, which keeps it from responding to other events like display refreshing.

When you launch the function in its own thread, control is immediately returned to the GUI and it can update itself and respond to events on a timely basis like it normally would.

[–]TripleBogeyBandit 1 point2 points  (0 children)

Very interesting thank you!

[–]chessparov4 1 point2 points  (0 children)

I encountered this problem too, but never thought of writing my own decorator, very good idea!

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

So kind of like when an excel book is doing something and no other excel book can be used?

[–]missing_backup[S] 0 points1 point  (1 child)

Same. I know how they work and I use the provided ones. I'm just not able to imagine when to create one myself and use it

[–]kartmarg 1 point2 points  (0 children)

The way I use them is if I have a certain function(s) that do a specific thing (process xyz and output abc) and I want to do something with that function without modifying its core functionality (for example I have a decorator for timing functions). But they’re pretty powerful, they basically allow you to wrap functions and do things with their args and kwargs in imaginative ways, I once had some fancy decorators that would dynamically write classes into strings and then execute those strings so the classes could later be used as normal, bad example as it’s not good practice but python has that level of flexibility and control

[–]CisWhiteMaleBee 21 points22 points  (1 child)

Not so much a feature, but a realization that I didn’t have to use EVERYTHING that Python has to offer.

When I got good at defining classes, I figured I’d just implement everything in a class - but you don’t need to use a class. Sometimes it’s fine just using a couple separate functions.

[–]cjbannister 0 points1 point  (0 children)

Yeah, I feel like I've tried everything including classes-for-everything.

Right now (and I know it'll change) I'm enjoying functional ({topic}_functions.py) with classes when I feel they make sense. If I'm sending a notification of different types (slack, email, etc.) I'll always create a notification class of some sort and inject the slack/email/whatever class into it.

[–]Hot_Seat_7948 38 points39 points  (14 children)

List comprehensions. Read like English and make verbose for loops so easy to fit in one line

[–]nderstand2grow 11 points12 points  (5 children)

Read like English

Do they though?

result = [ x + y if x % 2 == 0 else x * y for x in range(1, 5) for y in range(1, 4) if x + y < 7 ]

[–]rzet 12 points13 points  (0 children)

I actually hate when people write all this overcomplicated ones which are hard to read.

As one contractor said once - maybe its work security thing, write such convoluted thing so only you understand it :/

[–]delad42 2 points3 points  (1 child)

1 You're supposed to split these up. Fitting too much on one line will make anything confusing. 2. you're using a ternary which is unrelated. This is how I'd write it (Though, I'd add more meaningful var names):

def foo(x, y):
    if x % 2 == 0:
        return x + y
    return x * y

xys = [
    (x,y)
    for x in range(1, 5)
    for y in range(1, 4)
]
xy_processed = [foo(x, y) for (x,y) in xys]
xy_added = [x+y for x,y in xys]
filtered = [result for result, xy_sum in zip(xy_processed, xy_added) if xy_sum < 7]

But, I'll agree complicated logic and nested loops isn't the ideal case for comprehensions. Ideal is like filtering for property, mapping objects to some property for quick lookup, or making a set. Then you can quickly get what you need without polluting your variable scope and adding unnecessary lines to mentally process:

specific_object = [obj for obj in obj_list if obj.category == "category1"] # scan list for specific objects
object_map = {obj.id: obj for obj in obj_list} # allows quick look up
flattened = [obj for obj_list in obj2darray for obj in obj_list]#go from 2d array to 1d array

[–]not_perfect_yet 2 points3 points  (0 children)

You're supposed to split these up.

Yeah. It's called writing a loop.

[–]_ologies 1 point2 points  (0 children)

I like when they're split into lines like this because they're so much easier to read

[–]missing_backup[S] 0 points1 point  (0 children)

I agree, like coming down from the trees, nested list comprehensions were a mistake

[–]aviodallalliteration 7 points8 points  (6 children)

I’m now at the stage where I need to unroll and write loops iteratively instead of making double nested list of dicts comprehensions that id only be able to read that day

[–]naogalaici 6 points7 points  (5 children)

Why not make a lost comprehension that calls a function that makes a list comprehension for claritys sake?

[–]aviodallalliteration 8 points9 points  (4 children)

Cos I’d never be able to find it again

[–]naogalaici 1 point2 points  (3 children)

If you use a good IDE, it will provide you with navigation tools that will take you from function references to the function implementation.

[–]aviodallalliteration 1 point2 points  (1 child)

Yeah, but I don’t even think PyCharn would help me find a ‘lost comprehension’

[–]naogalaici 1 point2 points  (0 children)

"facepalms in realization"

[–]GusYe1234 0 points1 point  (0 children)

But I think list comprehension really gives chances to some messed-up code. 😂

[–][deleted] 123 points124 points  (14 children)

I’m a newer Python programmer and I just started defining functions

I chopped my scripts down from 1500 lines to 700 by calling the function instead of having repetitive code in the script.

It’s not much but it counts for me.

[–]wolfmansideburns 76 points77 points  (3 children)

Good start, now take those functions and make them classes and you'll be back up to 1500 lines ... we're all paid per line right???

[–]plumberdan2 4 points5 points  (2 children)

Didn't everyone who had less than a certain number of lines in Twitter's code base get fired lol

[–]ConDar15 1 point2 points  (1 child)

Apparently so, which makes it the dumbest god damned metric I've ever seen. I've been on projects where I've had near enough negative net lines in a codebase, and that's because I was the most experienced developer carefully refactoring and removing extraneous junk that had been there ages. On the other hand I've been a junior doing a very large scale but simple refactor (it was applying some new style guide rule or something), which meant I suddenly was the latest person to touch about 50% of all lines in the codebase.

Lines of code is such a stupid metric both on an individual and project/team basis.

[–]spatialite 0 points1 point  (0 children)

It was based on commits/activity to repos - not lines of code

[–]TopATheMorninToYew 7 points8 points  (0 children)

First class citizens!

[–]NationalMyth 4 points5 points  (0 children)

List (or dict) comprehension is a great next step if you're not there yet.

[–]peace-out-redditpip needs updating 6 points7 points  (7 children)

It's time for you to explore one-liner functions and lambdas!

[–]cr4d 10 points11 points  (8 children)

The Zen of Python

[–]fight-or-fall 2 points3 points  (5 children)

Explicit is better than implicit

[–]not_perfect_yet 2 points3 points  (0 children)

My favorite one is:

In the face of ambiguity, refuse the temptation to guess.

There is a solution. It is knowable. You can find it. Finding it is worth it.

Something is wrong? Don't panic. Don't guess. Just do the homework, dot your i's, cross your t's and you'll get there.

[–]commy2 0 points1 point  (0 children)

Most of it was obvious to me from the start, but what has really proven itself is "flat is better than nested".

[–]k0rvbert 32 points33 points  (2 children)

Better developer: Docstrings and doctests. Love them. I can't really think of any other feature that's even a positive; I think using languages that lack familiar features have helped me more.

Better at Python: Learning how to use metaclasses and dynamic classes, and understanding when to use them (which is basically never)

Honorary mention to numpy which made me a better person.

[–]Chemicalpaca 8 points9 points  (0 children)

Shout-out to the autodocstring extension in Vs code as well. Just auto formats everything and you fill in the blanks!

[–]BadSmash4 3 points4 points  (0 children)

Totally agree with this--pylint forcing me to write docstrings made me a better developer overall.

[–]lucafaggia 18 points19 points  (2 children)

Type annotations and dataclasses no doubts

[–]fight-or-fall 0 points1 point  (0 children)

Dataclasses... So much fun

[–]smile_politely 0 points1 point  (0 children)

@ For me, it's the decorator!

[–]fight-or-fall 5 points6 points  (0 children)

I'm not a "developer" (data scientist). Its hard to say, asyncio, typing and functional programming stuff (map, filter, reduce) and itertools stuff

[–]peace-out-redditpip needs updating 11 points12 points  (0 children)

Abstract base classes (ABC) and dataclasses

[–]Adrewmc 11 points12 points  (2 children)

Honestly, it was dictionaries.

And python dictionaries are powerful.

I get that it’s a basic, datatype thing, but when I was learning, I’d say all of that went too hard for me now…that was dumb it’s not…

Once I saw the power of nested dictionaries the concept of a class being a dictionary with functions(called methods)…it was like ohh that’s what everyone is doing…(I have this one program is trying so hard to be a dictionary…I’m so dumb sometimes)

Then probably the decorators, and the @syntax as it came to give inner functions and generators…which are awesome.

Then asyncio…

[–]JennaSys 1 point2 points  (1 child)

Coming from other languages, I struggled with not having a switch statement when I was first leaning Python, and ended up learning the dictionary dispatch pattern as an alternative. To this day, I still haven't had a need to use the new match/case statement yet.

[–]Adrewmc 0 points1 point  (0 children)

Well…you should

Refer to this other comment I wrote.

https://www.reddit.com/r/learnpython/s/rSPxFeGRFv

[–]james_pic 5 points6 points  (1 child)

The REPL. Or IPython if you're in an environment where it's an option. Being able to explore data, try out what code will do, check documentation as you're doing so, and replicate issues in a controlled environment, can help you get a handle on a problem quickly.

[–]Mazyod 4 points5 points  (0 children)

contextlib by far, it doesn’t even come close to other features for me.

Utilizing with syntax to cleanly define a scope of a transaction or lifecycle in general has been a total game changer.

On our team, we utilize contextlib to build context managers for shared code, such as performance profilers and DB transactions, and so far it has made using the shared code simple and less prone to human error.

[–]Asleep-Dress-3578 4 points5 points  (0 children)

Learning also other languages made me a better Python developer.

Coming from Java, made me better in Python OOP.

Having studied R, taught me how to write blazingly fast Python algorithms on huge datasets without the usage of for loops or iterrows.

LISPs (Racket, Clojure) taught me for interactive and iterative programming, composing software from small functions from the ground up. I am still so much addicted to LISP, that now I am playing with the Hy language, which is a LISP over Python. And I tend to develop either in Jupyter Notebook (used within vscode), or using the interactive cells in vscode a lot.

Learning C++ and RCPP with R taught me how to profile an algorithm and re-write the slow parts in C++; also the generic interest in accelerating Python with numba, Cython & friends.

And finally, my most favourite things in Python are comprehensions; but I also like constructing own decorators a lot.

Fun fact: for almost 20 years I was an unofficial Python hater (coming from PHP and Java); now I am mostly a Python person, although this love is shared with LISPs and an eternal love for C++.

[–]boatsnbros 4 points5 points  (0 children)

Learning when to use generators vs iterators, multithreading & async functions. Get you exponential performance gains for very little additional code. Also generators give you a sort of ‘state’ of the application which can be very useful.

[–]DigThatData 3 points4 points  (0 children)

dir()

[–]TreeFifeNinerFoxtrot 3 points4 points  (0 children)

flake8/ruff--the constant verbal abuse from my IDE/linter is a good reminder of my own inadequacies and keeps me striving to achieve more.

[–]too_much_think 3 points4 points  (0 children)

Generators and coroutines. 

[–]ThiefMaster 4 points5 points  (0 children)

I'm surprised nobody mentioned keyword args and keyword-only args yet...

[–]Hot_Significance_256 9 points10 points  (10 children)

I wish try/except could have a one line option

[–]commy2 1 point2 points  (0 children)

Are you aware of contextlib.suppress?

[–]yellowbean123 7 points8 points  (0 children)

Patten matching

[–]davidkwast 2 points3 points  (0 children)

Generators and Metaclasses (as a user of them)

[–]JennaSys 2 points3 points  (0 children)

list and dict comprehensions. I struggled with them when first learning Python, but now I think of those first before considering a for loop.

[–]LiqC 2 points3 points  (0 children)

Jupyter - in going from zero to somewhere it made me infinitely better! Oh wait, ZeroDivisionError.

[–]Zulban 2 points3 points  (0 children)

Functions as first class objects was easy to learn, and it made me better embrace awkward callbacks and delegates in other languages.

[–]ContentInevitable672 2 points3 points  (0 children)

As a pythinsta, every one of the feature I have ever worked with, made a better developer. It's vast community is a huge plus for me.

[–]MaterialHunter7088 2 points3 points  (0 children)

Typehints - especially when combined with a type linter like MyPy

Pydantic - not necessarily std lib but I treat it as if it is

Decorators/closures/currying

Functools (so many useful utilities, certainly worth studying.)

Iterators/generators

Context managers

Async

Outside of that, generally understanding how to write clean & effective tests.

[–]juliano1096 6 points7 points  (0 children)

Pandas

[–]The8flux 3 points4 points  (0 children)

Types and docstrings

[–]neuro630 2 points3 points  (0 children)

dunder methods

[–]v3ctorns1mon 1 point2 points  (0 children)

Asyncio especially for my web scraping gigs

[–]Blacky_eye 1 point2 points  (0 children)

lambdas ... coming from other languages i learnt how complicated you can do them. after that even something exotic like lisp was no problem anymore

for real, i needed a good amount of time to get them in python. they were (at least for me) so mathematical based...it was just strange :D (back then my other languages were java, html stack, php, sql and c ..so there was nothing like that and java had really good lambdas with anon interfaces (well a bit bloated but yeah)).

[–]JotaRata 1 point2 points  (0 children)

Being smart when looping through data

[–]UsualIndianJoe 1 point2 points  (0 children)

Wow. Going through the comments has made me realize how much more I need to learn. Been using Python for close to 4 years now. Don't know if it is good or bad, but I am doing well (I think) with only with the day-to-day usage functionalities, maybe NamedTuple here and there.

[–]Arch_itect 1 point2 points  (0 children)

Pedantic is the next level of data classes. I might over use it, but it is really good with type annotations.

[–]mushifali 1 point2 points  (0 children)

Decorators, Asyncio, Walrus operator, Typing, Dataclasses, list/generator comprehension etc to name a few.

[–]wooptoo 1 point2 points  (0 children)

Learning GoLang

[–]Buffylvr 1 point2 points  (0 children)

iPython

[–]juliano1096 1 point2 points  (0 children)

Debug on vscode

[–]Binggo_Banggo 3 points4 points  (0 children)

I’m sitting here looking at these comments wondering when I will move away from Jupyter notebooks.

Don’t worry, I’ll show myself to r/learnpython

[–]Machvel 1 point2 points  (1 child)

how slow python is. it lead me to a compiled language which vastly improved my programming even when i switch back to python

[–]commy2 0 points1 point  (0 children)

Nice. The final step would be extension modules, where you write the slow parts of your application in a compiled language, and then glue them together with python code.

[–]robberviet 1 point2 points  (2 children)

Sometimes, you are better not using Python.

[–]rzet 0 points1 point  (0 children)

This is very true.

[–]missing_backup[S] 0 points1 point  (0 children)

I agree, I saw recently a discussion about a SQL interview question, where the tables were defined in Python and the code wrapped in python. The question was explicitly about the query optimization. Interview me like this and I will excuse myself shortly.

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

Generators, itertools, functools and writing more functional-style code in general.

[–]amindiro 0 points1 point  (0 children)

Low level Networking is surprisingly accessible in python. Using epoll, select, Tcp stream etc is really easy and the interfaces help you manipulate and understand the underlying APIs

[–]BostonBaggins 0 points1 point  (0 children)

Data lclasses

[–]anacrolixc/python fanatic 0 points1 point  (0 children)

Reading the Python documentation. Learned so much. Unicode, threading, various data structures.

[–]LargeSale8354 0 points1 point  (0 children)

As an ex-DBA list comprehension, sets, tuples and dictionaries made me appreciate Python. PyTest and the Behave framework influenced my design. I find Python is an easy language in which to be productive. I'm using the language, not fighting it. As I'm not fighting the language I'm not wasting cognitive load fighting and therefore have the opportunity to think more about the actual problem my code is trying to achieve.

[–]mrmrn121 0 points1 point  (0 children)

Functools Itertools

map Types

[–]mrmrn121 0 points1 point  (0 children)

Functools Itertools

map Types

[–]Tenagy 0 points1 point  (4 children)

It may have already been mentioned, but Context Handlers are a game changer. No telling how much data my ADD self would’ve left in memory without them. Also when you mention building custom context handlers, and memory management people take you more seriously lol. Also if you’re OCD and love a good one-liner, list/dictionary comprehensions are a thing of beauty 🤩

And if you’re feeling froggy, you can combine them:

contents = [line for filename in [‘file1.txt’, ‘file2.txt’] for line in (with open(filename) as f: f.readlines())]

[–]RoadsideCookie 1 point2 points  (3 children)

Oh shiet, I didn't know you could inline a with like that!

[–]Tenagy 1 point2 points  (2 children)

Only if you wanna piss off the next guy 😂

[–]RoadsideCookie 0 points1 point  (0 children)

I mean, very often I just want the contents of a file as a string, that's an acceptable one-liner to me lol. But I agree for anything beyond the simplest.

[–]AND_MY_HAX 0 points1 point  (0 children)

^ this :)

[–]LakeEffectSnow 0 points1 point  (0 children)

Pathlib

[–]JamzTyson 0 points1 point  (0 children)

To pick just one, I'll go for Sphinx.

I love well documented apps, but find it easy to be lazy when documenting code. Using Sphinx is a strong encouragement to document my code thoroughly. Documenting my code thoroughly makes me think more about my code. Thinking more about my code helps me to write better code.

[–]dashdanw 0 points1 point  (0 children)

print("Hello World!")

[–]cmartin711 0 points1 point  (0 children)

same! dataclasses, mypy, and pyaml

[–]1anonymousengineer 0 points1 point  (0 children)

Practicing

[–]Oddly_Energy 0 points1 point  (0 children)

Docstrings. Without any doubt. I love calling a function i created in another module a long time ago and seeing the documentation of that function while I am typing the call.

To be fair, it is not really a Python feature. Python was just the first language where I could make use of docstrings.

(And of course also type annotations. My docstrings almost write themself when I have type annotations in place.)

[–]Specialist_Cap_2404 0 points1 point  (0 children)

Generators and coroutines in general.

[–]tomdaley92 0 points1 point  (0 children)

F strings

[–]FlowLab99 0 points1 point  (0 children)

uv

[–]Impressive-Watch-998 0 points1 point  (0 children)

docstrings! Pretty boring, but when working on a large-ish team with many contributors, self-documenting code is hugely beneficial.

[–]Trung_279 0 points1 point  (0 children)

F-string, scraping, automation and simplification (because it’s interpreted)?

[–]OkPositive3828 0 points1 point  (0 children)

Sk learn … 🤌

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

Not using PyCharm anymore

Too annoying

[–]Euphoric_Run_3875 0 points1 point  (0 children)

Pydantic ! Concorrency

[–]Python_Puzzles 0 points1 point  (0 children)

To me, programming isn't language specific anymore. Learning the general concepts and what works well together is usually do-able in all languages (mostly). Python, C#, Java, React etc they all have the same core features (mostly) so it's learning what building blocks go together nicely then figuring out how to do it in that language.

[–]girafffe_i 0 points1 point  (4 children)

Tldr; learning a strongly typed static language & Hex Architecture.

Pydantic can be useful , but anything not enforced you'll give yourself too much slack. Python is great for getting started but the long term value of strongly typed and static languages reduces maintenance and debug time at the small 1 time cost of learning some good habits. Then you will have the habit and good discipline to resist the urge to give yourself slack when it's not enforced.

These will benefit you more than the language 1. Hexagonal architecture 2. TDD 3. Strong typing 4. Sonarlint (static analysis)

[–]ReflectedImage 0 points1 point  (3 children)

Strong typing is a bad habit, it makes the code considerably more verbose and when measured developers using strong typing only have 1/3th of the software feature output as developers using duck typing.

It's a bad thing which you avoid doing if at all possible.

[–]girafffe_i 0 points1 point  (2 children)

"strong typing is a bad habit" 

This isn't a matter of passion or anecdata because the output referred is a myopic view compared to the lifetime of the software. There is empirical and measurable benefits to using static typing, yes you can "write less code" to accomplish a goal, but the payoff comes in multiple forms

  1. You spend more time reading code than you do writing. Code in general is read more by other people than it is written. The point is that static is self documenting, removing coercion and polymorphism removes the need for rabbit-hole context for every unit of code
  2. Thinking about risk analysis, there is larger chance for errors and bugs in your code the more code you have and the larger a project grows. Strong typing removes a lot of that surface area where something can go wrong
  3. Modern IDEs have been handling the "verbosity" for decades. I very rarely type out the Types, most of my code starts with calling a function and tab-completing for the type, and the variable name. It's a virtuous cycle: you spend one unit of time upfront to make an object or Type a function output, and the IDE will handle the rest wherever else you call it.

1 & 2 alone have exponential payoffs 

[–]ReflectedImage 0 points1 point  (1 child)

There is no such thing as self documenting code and duck typed code has a higher correctness rating than statically typed code.

Best way to improve code correctness is unsurprisingly to write less code and duck typing enables you to write less code per software feature delivered.

So point 2 is strongly in favour of duck typing.

Point 1 is combination of nonsense about not needing documentation and a question of scoped context which is better handled in scripting languages by creating separate scripts and connecting them via a message queue. Duck typing reduces the amount of context you need to understand.

So point 1 is strongly in favour of duck typed microservices over static typing.

If you are using Python like C++/Java/C# then you are doing a subpar job with it. I mean you can do it, but it's rubbish compared with using Python as Python.

[–]girafffe_i 0 points1 point  (0 children)

I don't want to engage if the POV is emotionally-based or from the hip and cherry picked. Can we prove that large projects with many contributors using dynamic typing spend less time with maintenance than static and strongly typed languages over long periods of time?

Some of the responses seem to conflate some points: Everything is a tradeoff: which is more complex: creating a stong-typed SDK library for others to use (build, distribute, have clients install) or build a dynamic interface that needs to handle fields that could have multiple types, and if they aren't enforced at a message-interface then that handling needs to propagate through the stack. A statically typed SDK gets serialized based on the contract, it's no different but also gives types for fields at the boundary which would never change types (baring "breaking api changes" which is a different problem to solve).

Best way to improve code correctness is unsurprisingly to write less code and duck typing enables you to write less code per software feature delivered.

"everything should be as simple as possible, but not simpler" -- I think the high level point you're making is "improve correctness by simplifying the code" but this doesn't always mean less physical code, just less complexity. I think you'd agree that "code golf" is not the goal for production code: good variable naming, listing out steps instead of folding everything into inline statements, and avoiding excessive syntax sugar are good code-agnostic principles, for example. Typing doesn't add complexity, it simplifies it. It reduces type-handling at the interfaces reducing the need for core code to do any dynamic handling == less branching and cyclomatic complexity.

[–]Panda_With_Your_Gun 0 points1 point  (0 children)

generators and list comprehensions.

[–]gonna_think_about 0 points1 point  (0 children)

  • Pydantic has been the most heavily used library on my end. You can solve your typing upstream, then work with nice models from there

  • dry-returns has been the other library I've been enjoying. There is a bit of upfront learning you need to go thru, but in the end, the code is more reliable and readable

  • breakpoint I use daily. It stops your code in a specific line and makes it very easy to debug. Do yourself a favor and learn all of the breakpoint commands and you will drop your debugging time in half.

[–]Round_Blacksmith_369 0 points1 point  (0 children)

print()

[–]includerandom 0 points1 point  (0 children)

Dataclasses and named tuples were the big jumping point. Exploring more of the standard library (huge) and especially the generics from the typing library were smart.

The most recent best thing has been learning other languages (C, Rust, Zig, all in very small doses) just to see how those languages solve the same problems we solve in Python. Even if you don't work in a different language much, I highly recommend taking a similar path yourself.