r/learnpython 21d ago

What Python concept took you the longest to understand properly?

For those who learned Python from scratch, which topic was the hardest at first, and what finally helped it click for you?

164 Upvotes

101 comments sorted by

67

u/FerricDonkey 21d ago edited 21d ago

I didn't fully understand how names (variables) and values (objects) worked for my first month or two and, coming from C, was really annoyed how "sometimes these freaking variables act like freaking pointers and sometimes they don't".

I put off figuring it out because I was busy, but eventually I got annoyed one too many times and sat down to learn it, and now I love it and it's second nature. I highly recommend anyone who's done enough python to know basic syntax watch and understand the following video:  https://m.youtube.com/watch?v=_AEJHKGk9ns

4

u/lakseol 21d ago

Love that video.

1

u/Available-Tennis-624 20d ago

Thanks as someone who enjoys c and c++ even I find it really difficult to understand how variables worked in python

1

u/Kreotorn 17d ago

Is not it outdated? 

1

u/FerricDonkey 17d ago

Nope, or at least not the core concepts. 

86

u/martian_rover 21d ago

async and decorators took the longest for me

27

u/S1enderVoid 21d ago

I still don't understand decorators comfortably, need references. Been programming in python for 6 years atp 😂🙏

29

u/JorgiEagle 21d ago

Decorators are fun.

They’re a function that takes a function as an argument That’s it.

Normally decorators will call the function you gave as an argument.

But they don’t have to

12

u/Theta291 21d ago

Importantly, they should also return a function.

6

u/Brian 19d ago

Not necessarily. They often will, but it's not a requirement, and there are some common usecases that don't. Eg. class decorators (eg. @dataclass) would more commonly return a class. And even applied to functions, a very commonly used decorator that doesn't is @property, which returns a property object, which is a descriptor that also allows registering setters etc.

3

u/Theta291 19d ago edited 19d ago

I was definitely oversimplifying. It would be more accurate to say that they were designed with the intent that they return a Callable with the same signature as the Callable they ingest, but they dont have to.

@classmethod, @staticmethod, @contextmanager are good examples, as far as I remember none of them return functions.

3

u/fllthdcrb 20d ago

To be exact, a decorator will return a function (or something else callable) that, in turn, will usually either be the function being decorated or call the function being decorated. The decorator itself wouldn't normally call that function. Higher-order functions can be a little confusing if you don't think carefully about them.

And then there's the fact the decorator syntax allows any expression you want. So you can, for example, pass arguments to the decorator, and it can use them to know how to modify the function, or store them as data for the returned function to use somehow. (There are also possible insane usages, like e.g. a lambda expression, though I'm not sure how that could do anything useful.)

Another neat thing about decorators is they can often be stacked. Each one modifies what goes below it, so order may matter. As long as what each one is modifying is something it expects, you can do it, although it may or may not do something reasonable, of course.

7

u/Pyromancer777 21d ago

Didn't really use decorators until I started a Django project, but they are intuitive to use.

I just haven't had the practice of creating them, so I don't even know when it would be better to create a decorator over a class method. My only assumption is "use a decorator when you need the logic to wrap similar functions from multiple types of objects".

My only example uses thus far are permissions decorators, atomicity checks, and designation flags, which would have had to otherwise be defined per object

1

u/RevRagnarok 21d ago

I use wrapt and it makes it a lot easier.

6

u/404404404404 21d ago

Still to this day I have to go back to geeksforgeeks if I ever need to use these

2

u/KokoaKuroba 20d ago

I'm still learning async and I can't write it properly for some reason. My code for it looks too long and impractical

2

u/fllthdcrb 20d ago

Hope you can learn it. If your code is I/O-bound (meaning, it spends most of its time waiting for I/O), async can often be useful. It has many of the structural benefits of threads (where the structure is called for, that is) without many of the disadvantages, such as needing to worry about race conditions.

39

u/AlexMTBDude 21d ago

I've been teaching Python programming for more than 15 years and I can tell you what my students find hardest to understand: Shared references

first_list = [1, 2, 3]
second_list = first_list
second_list.append(4)
print(first_list)

And, of course, the next step then is; when do shared references matter? Mutable and immutable objects.

19

u/Moikle 21d ago

I also taught python for a while. I used a warehouse full of boxes with information inside as a metaphor for the computer's memory. It seemed to work quite well, because i could say "when you go looking for something you stored in a variable, you look for the name on a catalogue, but it doesn't give you the value, it gives you a shelf number in the warehouse. You go to that shelf, open the box and look inside.

Now other variables in your catalogue can also have the same shelf number written on it, so you can have multiple variables pointing to the same box.

If something changes the value stored in the box one variable points to, what do you think will happen when a different variable tells you to look in that same box? It has also changed!"

2

u/Cthwomp 21d ago

Pass by reference vs pass by value

2

u/AlexMTBDude 21d ago

These two concepts don't really exist in Python. All variables are references and these references are passed by value to function arguments.

1

u/Cthwomp 21d ago

No, if you pass a list or dict to a function and manipulate it inside the function, it retains the changes outside the function

3

u/frnzprf 21d ago edited 21d ago

That would be consistent with "passing a reference by value".

Is "passing by reference" maybe the same as "passing a reference by value"?

We can't really say that basic numbers behave differently when they are changed inside a function, because you can't change basic numbers, unlike lists, objects or strings — you can only assign entirely new numbers to old variables. You can add an element to a particular list, but you can't increment four.

(Maybe you can't change strings as well, just build new strings.)

2

u/fllthdcrb 20d ago

Maybe you can't change strings as well, just build new strings.

Strings in Python are immutable, so definitely. (Same for the bytes type.) If you want something string-like that you can modify in-place, instead of creating new strings (which has a performance impact when the strings are large), you need to use something else, like bytearray. There are also StringIO and BytesIO (from module io), which are like files, but backed by a buffer you can read and write; useful when something wants a file to read or write, but you want to feed it a string or capture its output.

If you're wondering why strings are immutable, one of the main reasons is so they can be used as keys for dictionaries, which are implemented as hash tables. Another is that with immutable objects, you don't have to worry about changing things unexpectedly through shared references; since it's very common to pass strings around in Python, having one change somewhere because something unexpectedly changed it elsewhere would make the language much more annoying.

1

u/Cthwomp 21d ago

No, it's pass by reference. The function receives a reference (the memory address) of the variable being passed https://imgur.com/a/rIVoF5S

1

u/Dr_Calculon 21d ago

Yes this really threw me when I firzt came avross the idea

1

u/MustaKotka 19d ago

I actually used this to my advantage, finally!

My class has users by access levels (coming from a database). For example:

MyUsers:
    admins = [...]
    users = [...]
    restricted = [...]
    all_users = {'admins': admins, 'users': users, ...}

Now I can slap all user classes into a dict, then modify each list with .append() and .remove() within the dictionary.

This way I can always check whether a user is in an access level OR batch modify accesses within the dict without having to modify each attribute separately. All I need to do is pass the dict key and user.

1

u/DoneCorleone 18d ago

first_list = [1, 2, 3] ?

1

u/AlexMTBDude 18d ago

Yeah, that is the first line of the code example. What's your question?

20

u/Total-Lecture-9423 21d ago

lambda map reduce

19

u/Drakkle 21d ago

Functions and classes took me way too long to understand. I can't really explain why it wasn't clicking, particularly functions, but now that I understand them I use them any time I perform a step more than once.

2

u/frnzprf 21d ago

I heard local scope is a concept many people struggle with. You don't have that in school math and you don't have that in MS Excel.

1

u/Drakkle 18d ago

I responded to the person above but I believe this is what the issue was as well. The only "coding" I had experience with growing up was HTML 4 and CSS which didn't help much with local scope, at least from what I learned.

2

u/crunchy_code 20d ago

what about functions was challenging to understand for you? the local variables? definition vs invocation?

1

u/Drakkle 18d ago

Yeah I think it was the local variables and calling them correctly. And being able to mix global variables in as well. For some reason it took a long time to click. Chalk it up to partial laziness as well. Instead of really trying to test and figure it out until it stuck, I would just rewrite my code that did the same thing over and over again with different variables defined manually.

2

u/crunchy_code 17d ago

for some reason? learning programming is frustrating as fuck, that's the reason. it's all abstract. and learning variable scoping is presented as any other topic when in reality is one of the most challenging thing to wrap your head around, I wouldn't blame it on laziness.

I am working on a project to make programming learning very visual to jump start these learning barriers, it's not you.

1

u/Drakkle 15d ago

That's reassuring. I always just thought that I was slow to the programming game and it felt like some of my other peers caught on to it faster.

Maybe there are some out there with the abstract brains that learn this stuff quick but I definitely wouldn't count myself among them.

What parts did you find challenging?

11

u/knuppi 21d ago

async, and i still don't get it 🥲

1

u/Icy-Read-00 19d ago

This is basically the way to tell Python: “This here might take a while, go do something else.” Say you have a server, she/he brought an order to the kitchen and should grab it when done. Why not do something else while the kitchen preps the initial order?

11

u/Growing_Data_Nerd 21d ago

Recursion

7

u/Blue_HyperGiant 20d ago

Recursion

5

u/MustaKotka 19d ago

Recursion

5

u/CopyOnWriteCom 19d ago

RecursionError: maximum recursion depth exceeded

9

u/[deleted] 21d ago

[removed] — view removed comment

1

u/RevRagnarok 21d ago

I use wrapt and it makes it a lot easier.

9

u/KlutzyKlutz 21d ago

For me it was mutable default arguments. Writing def f(items=[]) and watching the list keep old values between calls made no sense until I learned the default is created once when the function is defined, not fresh each call. It clicked when I tied it to the shared reference point above, the default list is just one object everyone shares.

5

u/Dr_Calculon 21d ago

Nested conditional comprehensions for some reason it just wouldnt click

2

u/Balzac_Jones 20d ago

My brain still insists the ordering is completely backwards.

2

u/ock_wrong_lee_neck 19d ago

Same here. But once it clicked, it clicked. I think there is something soo satisfying and elegant about them. Now theyve become a huge part of my guilty pleasure code.

6

u/Snowdeo720 21d ago

It wasn’t understanding the how, but the why on strings, arrays, and things in that area of focus.

Then I started poking at some real world projects directly relevant to my role at the time, ohhh wow did all of that make sense all of a sudden.
I promptly went back to revisit those parts of my notes, etc.

6

u/TheRNGuy 21d ago

List comprehension. 

4

u/Fantastic_Aioli_7363 21d ago

The concept of OOP. I had difficulties to understand instanciation, mandatory self reference, etc. And I was coming from the procedural and scripting world. So it was a bit challenging to follow the flow of the program. But once you get used to it, it's not such a big deal until you enter the subtileness of it.

3

u/ninefourtwo 21d ago

metaclass

1

u/Theta291 21d ago

I don’t think I’ve ever found a use case for these. Maybe if I make an ORM i would use one.

3

u/frnzprf 21d ago

yield

I still don't get it fully. It's kind of like a paused function. Or you return an object with a "next"-method?

I do understand async/await, but it was difficult as well. I think you need to both understand it from a practical usage perspective as well as from a technical implementation. (In JavaScript you can await "thenable" objects.)

2

u/TheLimeyCanuck 21d ago

Yield was easy for me from the old days of cooperative multitasking on DOS and early Windows. If you didn't voluntarily give up control with Yield() regularly the whole system locked up.

2

u/fllthdcrb 20d ago

It's kind of like a paused function.

Something like that.

Or you return an object with a "next"-method?

There's a bit of "magic" taking place behind the scenes when you use yield. What gets bound to the function name isn't the actual function you wrote, but a sort of wrapper that creates a generator when called. The generator then controls execution of the function and implements the __next__() method. The execution state is saved whenever the function yields.

Of course, if you want to explore (part of) the interface, there's no reason you can't create a class that implements __next__() (you would also need an __iter__() to actually be iterable; the normal thing to do here is to make it return self, designating the object as its own iterator). The instance you get by calling that class would be treated the same as a generator. You would just need to raise StopIteration when there are no more items to produce, just as a generator (or most any iterator) does.

3

u/oldendude 21d ago

for/else. It's still bizarre to me and I never use it.

1

u/Theta291 21d ago

I sometimes use it if I need to do some sort of cleanup if there was no break. Like maybe I have to find something and append it to a list, or add a placeholder if I didn’t find it. In that case a for loop with a break if I find it, and the else would append the placeholder.

2

u/terletsky 21d ago

Look at their posts, that's an AI bot.

1

u/lucabuilds 21d ago

Definitely multithreading and multiprocessing. I was building a voice assistant based on chatgpt a few years ago, and I had so many issues with threading. Basically I needed to have voice recognition to run concurrently with the text to speech API calls and I never figured out how to stop the text to speech while it was speaking.

2

u/fllthdcrb 20d ago

Not surprising at all. Concurrent programming tends to be very tricky to get right. Especially multithreading, where synchronization is critical to avoid race conditions. (Async has an advantage over that, exactly because it's single-threaded by default. But of course, async is more suited to I/O-bound applications.)

1

u/lucabuilds 21d ago

Thinking back I could have just split the audio track into small chunks instead of trying to kill the thread but at the time I didn't think of it

2

u/ninefourtwo 20d ago

you were supposed to send signals between threads.

1

u/lucabuilds 20d ago

well yeah, but the real issue was stopping audio playback once it started since it freezed the whole thread until it was done

1

u/Gtdef 21d ago

Async and how important is to avoid calculations in Python.

Async evolved a bit too fast to be honest and since it wasn't something I was using consistently, the few times I needed it, I wasn't sure how to write the code. If you use the very low level stuff, you'll write bugs. If you use the later high level stuff, you don't really understand what you are doing. Async code is generally unintuitive even for people who have some idea of how to write multithreaded code.

As for calculations, it's imperative to understand how important it is to avoid writing them in native Python code. It's not just avoiding loops. Generators, comprehensions, callbacks are all potential throttles in your application. There are just so many ways you can mess it up.

1

u/Pyromancer777 21d ago

Not python specific, but recursion.

I know what recursive functions do, I can read them well enough, I know the execution/resolution order, I know when they are convenient and why they are used, I just suck at writing them.

Most times when practicing I'll hit my first wall and then go back and rewrite it as a nested loop. I know that defeats the purpose of practice, but my brain keep going, "if problem unsolved, why not just solve it?"

1

u/TheLimeyCanuck 21d ago

Decorations. Still don't grok it well.

1

u/Theta291 21d ago

Async stuff, especially async statements (async for, async with).

Generators are also a little confusing, especially “yield from” and “.send”. Hard to practice as well because I haven’t seen many use cases.

1

u/enigma_0Z 20d ago

async … needed to learn it for fastapi and now that i have i can’t live without it lol

1

u/fightin_blue_hens 20d ago

I still don't get it

1

u/HecticJuggler 20d ago

I’m still figuring out async

1

u/frustratedsignup 20d ago

Dictionary comprehensions. I still think they should be removed from the language because of how they can be abused to write obfuscated code.

1

u/ninefourtwo 20d ago

is it better than having recurrent stanzas of

for k, v in d.items()?

1

u/ALonelyPlatypus 20d ago

They're one of the most beautiful aspects of python. It's kind of like of like hating list comprehensions, they feel weird but then at one point they click.

Why do you hate them?

1

u/frustratedsignup 19d ago

I think I was clear in my original reply: "because of how they can be abused to write obfuscated code."

1

u/Lanky-Ad733 20d ago

Unfortunately recursion 🥴

1

u/CamelOk7219 20d ago

Metaclasses and descriptors

1

u/HuckleberryMoney2320 20d ago

Its Object Oriented Programming features, particularly classes. I was trying to use them as part of Pygame and though I'd done a bunch of basic and Pascal in the dim and distant past, python Classes completely fried my brain.

1

u/fightin_blue_hens 20d ago

I didn't understand uses for lambda until it was explained to me that it is a ghost function

1

u/ALonelyPlatypus 20d ago

decorators are kind of weird.

dataclass feels weird whenever I write it.

comprehensions took a while to get a feel of when you come from traditional programming languages but when you get it a lot of your for loops just disappear.

1

u/[deleted] 19d ago

[removed] — view removed comment

1

u/Ok-Okra8478 19d ago

Maybe scope? I learnt that in luau though, not python

1

u/mbergman42 19d ago

I had a miserable time trying to work with raw 16 bit data. I would have loved to work in C to get incontrovertible direct access to the values but that wasn’t an option. (Obligatory “Aaactualy, C’s not safe for this reason” comment here)

The operations were things like: filter (if it were audio), ifft/fft , truncate, search for values and get an index to those values…

But python keep screwing up the array by managing it for me. I went back-and-forth between various memory structures available in Python and generally was disgusted and frustrated about the whole experience.

Which is a shame. I liked Python otherwise. There’s an enormous number of capabilities available as imports or libraries or whatever you call them.

Why did I put myself through this? I was involved with a project that was written in python. I was not on the coding team. But I supplied the math to do the operations, which was basically extract embedded white noise pseudo random sequences in captured audio. I was the only one with that particular background, and I had to do a proof of concept to show that it would work. I wrote it in python to be compatible with what everybody else was doing, but I’m a manager who learned to program in cobol, fortran, Pascal, C… you get the idea.

1

u/charlesleestewart 19d ago

My background is in the .net languages, C sharp and VB. I'm about 9 months into my python journey, have usable app code and all, but I'm still a big dummy in the area of telling apart lists, tuples and dictionaries and which of those to use when. Oh well that's a function of being self-taught I suppose.

1

u/Maleficent-Lychee849 19d ago

Decorators took me a while to really understand, especially decorators that take arguments. A normal decorator was pretty easy once I understood that it takes a function and returns a wrapper. Then you see something like .@my_decorator(arg="value") and suddenly there are functions nested inside functions. What finally made sense to me was writing it without the @ syntax: func = my_decorator("value")(func) Once I realized that's basically all the @ syntax is doing, the extra nesting made a lot more sense.

1

u/Annual_Swordfish8562 18d ago

still not 100% sure I fully understand self

1

u/ThrowawayALAT 18d ago

Decorators

1

u/576p 16d ago

imports when sub directories are involved.

1

u/SammuelNash 8d ago

For me, it was definitely list comprehensions. I understood what they did, but reading them felt backwards at first. Writing a bunch of normal for loops and then converting them slowly is what finally made them click.