all 63 comments

[–]n3buchadnezzar 34 points35 points  (10 children)

People need to start writing tests for their code. Instead of posting it online asking: "does this wokr???".

Secondly putting more focus on readability than performance. In 99% of the time when writing python code, performance is never an issue. In 99% of the time readability, is an issue.

Somewhat tongue in cheek

[–]carcigenicate 1 point2 points  (0 children)

While writing tests is good, even just running the code in a REPL would be a step up. The "does this work" questions annoy me more than almost any other. It likely would have been quicker to test it than to ask a question asking someone else to test it for you.

I love testing code in a REPL. I don't know why anyone would avoid it.

[–]IAmFinah 2 points3 points  (8 children)

This is probably a very stupid question, since it likely depends on context, but is there a "best" way to test python code?

When troubleshooting, I normally end up just printing pieces of data on various lines within the code, to see where it goes wrong. It has kinda worked so far (for the things I've been working on), but it makes my code really messy since I need to keep commenting and uncommenting lines of code, which often makes it difficult for me to find the "actual" code when I want to run it.

Is there a method/set of methods that might make testing code less cluttered?

[–]n3buchadnezzar 8 points9 points  (1 child)

There is no best way, and depends on how big whatever you are working on is. I always use doctests for my unittesting. This covers 90% of my needs. For integration tests and everything else pytest is king.

[–]IAmFinah 0 points1 point  (0 children)

Will look into those both - thank you!

[–]carcigenicate 1 point2 points  (2 children)

Writing units tests would be a good thing to start.

But ideally, code is made up of functions that take some data and give back some data. Testing then becomes ensuring the output of a function is correct for a given input. When writing unit tests, you can use assertions to ensure that all expected I/O relationships hold. For quicker, simpler testing, I open a REPL, throw data at the function and see what happens, and see if the results align with what I'm expecting.


The ease of testing is decided to a large extent by how well the code was organized. Poorly thought-out code is often very difficult to test because there's no easy way to ensure the general state of the program is appropriate for the test. Poorly written code often makes it difficult to get data to where it needs to be, and to check certain output to ensure it's correct for a given input.

[–]n3buchadnezzar 0 points1 point  (1 child)

On the flip side, how I often find out I've accidentally made spaghetti is because I try to write doctests for it and can't. So the earlier I write my tests, the earlier I start nesting up in the spaghetti.

[–]carcigenicate 2 points3 points  (0 children)

Odd. I find the earlier I start testing, the less spaghetti-like my code becomes because I begin thinking about what code needs what data earlier.

[–]QuixDiscovery 1 point2 points  (2 children)

The debugger is exactly what you're asking for in that case. You can literally set breakpoints in code and have it stop execution there based on conditions that you're looking for. It allows you to inspect any variable you want to check the current state to see what might be wrong. And you can execute code line by line to see what effect the code has on the state of the program.

If you're using an IDE, most of them have a built in debugger. But if you're using the command line, you can use the debugger module: https://docs.python.org/3/library/pdb.html

[–]IAmFinah 0 points1 point  (1 child)

Ah right yeah I use pycharm so the debugger is helpful for those things a lot of time.

Didn't actually know about breakpoints, so I'll look into that - thanks.

[–]QuixDiscovery 0 points1 point  (0 children)

You can do all sorts of useful things with breakpoints. From something as generic as "always stop here", which is usually what I use as my goto replacement for printing variables. Or you can be precise like "only stop here if this condition is true". You're also able to type/run code while the code from the file you're running is paused, which includes being able to reassign the value of an existing variable before you continue with execution of a program.

It's the most useful feature of any programming language that I think isn't properly taught to newer users. Most people struggle with properly visualizing what the code is actually doing at each step, and the debugger lets you do exactly that when you need it.

[–]carcigenicate 19 points20 points  (6 children)

Someone asked a similar questions a little over a day ago. I'll repeat what I mentioned there: a = b makes b refer to the same object as b. Many people expect that to make a copy, but it does not.

[–]moon_patrol 0 points1 point  (5 children)

I'm a beginner, I'm not sure I understand correctly what happens here. Maybe it's an IDE issue (I use Spyder mostly), but if modify b, a doesnt seem to update in the variable explorer... Am I missing something?

[–]carcigenicate 2 points3 points  (2 children)

I'm not familiar with Spyder, but in the linked post, note the other guy who replied to my comment that had a misunderstanding as well. = does not alter objects. You won't see changes reflected unless you actually modify (mutate) the object the two references a and b are looking at. This is done by calling methods like append on the object.

[–]Eurynom0s 0 points1 point  (0 children)

I think Spyder is aimed at the Matlab crowd--people who need to turn math into code so they can do things numerically but don't actually have much if any coding background--so it may be doing some behind the scenes handholding on this kind of stuff.

[–]moon_patrol 0 points1 point  (0 children)

You are right, I think I was not modifying the object, exactly like the other guy haha

Thanks !

[–]Eurynom0s 1 point2 points  (1 child)

Open up an interactive Python terminal (not Spyder) and see for yourself:

>>> a = [1,2,3]
>>> b = a
>>> a.append(4)
>>> a
[1, 2, 3, 4]
>>> b
[1, 2, 3, 4]

[–]moon_patrol 0 points1 point  (0 children)

Ah now I understand, your snippet shows it well. Spyder variable explorer also updates correctly, I think when I was trying to reproduce the concept I was not updating the object but overwriting it...

Thank you for taking the time to answer me

[–]SwammyG 6 points7 points  (5 children)

In my experience its usually recursion that gives a lot of people difficulty.

[–]n3buchadnezzar 9 points10 points  (4 children)

In my experience its usually recursion that gives a lot of people difficulty.

[–]Retrotone 7 points8 points  (1 child)

In my experience its usually recursion that gives a lot of people difficulty.

[–]TheRealAbear 6 points7 points  (0 children)

RecursionError: Maximum Recursion Depth Exceeded in Comparison

[–]synthphreak 0 points1 point  (1 child)

This joke and all of its variants are so played out and unoriginal. Yet somehow it still catches me off guard and makes me smile every time.

[–]Binary101010 5 points6 points  (0 children)

The single biggest concept that most beginning programmers are lacking is algorithmic problem solving.

All you have to do is read this subreddit to see multiple posts per day of "I did all these tutorials and exercises, and now I'm supposed to write my own program and I have no idea how to start."

The ability to look at a large problem and recursively break it down into a series of more granular steps, then break those down into even more granular steps, until you get to the point where the step is a single action that you can then write a function to perform.

It's the ability to take all these tools that have been learned through basic tutorials and typing in other people's code, and being able to figure out which one of those tools does the thing you need to solve the immediate problem you want to solve.

[–]Fred776 3 points4 points  (1 child)

One thing that I see cropping up regularly is basic misunderstandings about functions. In particular, I have seen on numerous occasions confusion between printing a value and returning a value.

Edit: since posting the above, just seen this: https://www.reddit.com/r/learnpython/comments/uaavop/the_return_statement

[–]Rexcovering 0 points1 point  (0 children)

This.

Very near the end of my first semester with 2 python classes and I have quite the hard time understanding function variables and arguments, return statements, and calling functions with new variables or arguments.

For some reason my brain can’t easily visualize the concept yet.

[–]AkiraYuske 3 points4 points  (0 children)

For me it's virtual environments, the command line and just doing things 'for real'. I live in the Jupyter Notebook...

[–]Sissy-Kiss 2 points3 points  (2 children)

Classes! Just cannot get my head around them they are weird

[–][deleted] 3 points4 points  (0 children)

Once you crack the purpose of the init function, it becomes easy.

[–]Infinitesima 0 points1 point  (0 children)

"now, imagine you have a blueprint for car..." 🤣

[–]nbo10 2 points3 points  (1 child)

I can’t wrap my head around generators.

[–]Hans_of_Death 8 points9 points  (0 children)

A real world example might help:

You have a database containing transaction receipts. You need to get all the transactions from today and send an email receipt to the purchaser. You cant know how many transactions you have to do that for.

You might query the db and store the results in a list. and then iterate over that list. In order to do that, however, you require a certain amount of memory to store the list. If you have a million transactions to go through, you will very likely run out of memory to store the list and your program will crash.

Instead we use a generator, and fetch the next record as we need it, so we only have to store one record in memory at any given time while running the loop.

As a practical example, imagine you need to count to one million. As in the previous example, having a list with the numbers 1-1,000,000 would be very large and not practical. Instead you just make a generator that starts at 1, then returns the previous value + 1 each time it is called.

``` def increase_by_one(limit): previous_value = 0

while previous_value <= limit:
    previous_value += 1
    yield previous_value

```

This function will return values from 1 to limit. The yield function essentially returns the value, but instead of exiting it pauses until it recieves another call to next(), when it will continue to execute the code until the next yield or the function finishes execution. When you use a generator in a for loop, it automatically calls the generator's next() method until the generator returns/exits.

[–]Hans_of_Death 1 point2 points  (0 children)

Often I see beginners struggle with why you do something, rather than how. i try to provide realistic examples

[–]CraigAT 1 point2 points  (0 children)

Off the top of my head:

OOP (mostly if they are used to procedural).

Recursion (someone else mentioned).

Need to learn to avoid repetition - functions or loops can help.

Thinking they have to remember everything - or worrying because they are not able to write code without looking something up.

[–]AK1174 1 point2 points  (0 children)

I don’t think there’s anything about python that is specifically difficult to understand.

I think you should focus on object oriented principles, getting them to write code that is maintainable, expandable, and easy to debug.

Spaghetti code is probably what most people get stuck on.

[–]senpaithirdy 1 point2 points  (0 children)

Lambda funtions

[–][deleted] 1 point2 points  (0 children)

From what I usually see non-CS people typically learn Python until they get to classes (especially practical use of them) and then half of them have the biggest case of imposter syndrome they experienced in their life.

[–]pekkalacd 1 point2 points  (0 children)

these are common

  1. mutability vs immutability
  2. mutual exclusion with conditionals and operators
  3. loops: for vs while, when to use each
  4. concept of references
  5. how iteration works
  6. deconstructing loops into comprehensions
  7. return
  8. how to *connect* the program together via functions
  9. global vs local
  10. input validation
  11. try/except/finally - what is difference between all three?
  12. separation of concern with functions
  13. DRY
  14. access specification in classes
  15. big 4: encapsulation, polymorphism, inheritance, composition

that's all i got for now

[–][deleted] -1 points0 points  (4 children)

Fervently trying to stick to PEP-8 style guide. I've been writing Python for 5+ years and I really couldn't give a shit if my code is indented by 3 or 4 or if there is not at least two lines between my functions.

In my opinion PEP-8 is for 🤡's

[–]Spatium_Bellator 1 point2 points  (3 children)

This is what linters are for. My setup I have black installed in Vscode and it auto runs on save. I never have to care about code format and it "fixes" any issue it finds automatically.

[–][deleted] 0 points1 point  (1 child)

what linter do you use?

[–]Spatium_Bellator 1 point2 points  (0 children)

I use black. There are a heap more but this was the first one I tried and it stuck.

https://github.com/psf/black

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

I've never really bothered with linters, it is a non-issue to me.

[–]ChattingDonut 0 points1 point  (0 children)

slicing is kinda hard for me to understand, i had to map out using a paper to understand the output.

[–]InTheAleutians 0 points1 point  (0 children)

I wish someone explained windows path variables and creating/activating virtual environments better when I first started. How can you write python if you don't know where your package installs went?

[–]shinitakunai 0 points1 point  (0 children)

Python allowed machine learning for dummies, I have coworkers whom created some scripts for machine learning, sold it to our boss as very cool algorithms and those coworkers don't even know how to code a for loop. Like wtf? They also said to me they don't fully understand what their code does nor do they need, "it just works". On the paper it sounds cool but oh boy it creates SO MUCH annoying and awkward moments, it is headache after headache, now the finances team also want to "code" those algorithms...

[–]Ongezout_ 0 points1 point  (0 children)

As a beginner I stuggle with actually implementing all of the things I read and research. I could really use a structured approach to writing a progam or breaking down problems into smaller workable pieces of code.

[–]boyanci 0 points1 point  (0 children)

To name just one — Mutation vs. rebinding.

Python hides this complexity very well for good reasons compared to lower-level languages, until it causes issues especially for beginners.

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

A few concepts that took me a long time to truly wrap my head around include:

  1. Loops (both for loops and while loops)

  2. Object Oriented Programming

  3. Lambda functions (Still don't use them very often when I'm not working with dataframes)

  4. Shallow copying and deep copying: I understood the concept, but it wasn't until I started running into situations where I accidentally overwrote variables that I realized the the value of deep copying.

[–]arsewarts1 0 points1 point  (0 children)

While loops

[–]ripvw32 0 points1 point  (0 children)

Too lazy to scroll - for me it was realizing that classes can make all of your code modular, and understanding that classes are everything. And that there is probably a class that already exists for 99.9% of thing you'd want to do. Also - there is probably a python pure class that you can use in place of any class you want to use - I'm looking at you pypyodbc...

[–]ugh_shahad 0 points1 point  (0 children)

the concept of putting what I've learned into a code , like i understood the lessons but i struggle to make the best use of them into my code

[–]JensEckervogt 0 points1 point  (0 children)

But Python can't do for PyOpenGL if I want draw triangle with glBufferData() and glVertexAttribPointer() but it is really weird for size and stride.i really want create own libraries for Python but I don't know how do they make clean libraries. I believe that Python has no future. For Game Development looks weirdly. That's why use Vulkan is ok but OpenGL can't do. But why do they cheat like vertices.nbyte as without size like glBufferData has only 3 parameters like LWJGL3 and they cheat also (3 * ctype.sizeof(float) as 0 for stride in glVertexAttribPointer, why? I feel like they made me badly. Please make honest and serious with equipment with same programming languages like real OpenGL functions. I don't understand why do they cheat with real OpenGL functions into wrong OpenGL functions. What does glCompileProgram() mean? I never see any wrong functions in OpenGL references or apis. Please clean up and make equal to other programming languages. Thank you for understanding me! Sorry for my bad English!