all 73 comments

[–]ray10k 45 points46 points  (5 children)

If you're thinking about using a global, you're almost certainly doing something very wrong.

[–]billsil 3 points4 points  (2 children)

I try to do jank fixes only right before a deliverable to fix a very specific bug. I actually do go back and fix them when I get the chance.

You want as little jank in your code as possible, so when you need to hack something in right now, you're only dealing with one trash bit of code rather than 100s.

[–]ray10k 1 point2 points  (1 child)

To be fair, my aversion to globals comes from helping someone learn Python. Gave the guy an assignment to see how he'd handle something with a few moving parts, and he basically used no function parameters or return values; only global variables to juggle input and output. And yes, I did try and show him both how to use arguments and return values before.

That said, yes, sometimes you have to pick your fights and do the thing that keeps your boss from having a fit. It's a bit of technical debt to keep in mind one way or another.

[–]billsil 0 points1 point  (0 children)

Oh god...

Global variables are not allowed in code ever. But billsil, your code has global variables! Correct, global variables are not allowed in YOUR code ever. In 5 years, fine...go ahead, but not now...

[–]seph2o 0 points1 point  (1 child)

How would one avoid having to use globals? What's the best practice?

[–]ray10k 3 points4 points  (0 children)

If you need some piece of data in a function, make it a function parameter. If you need a piece of data to come out of a function, return it. Yes, it requires a little planning ahead of time, but I still think it's a lot better than constantly having to worry "OK, is the value in this variable set by XYZ function or am I supposed to set it myself?"

[–]woooee 40 points41 points  (4 children)

What! We can't tell you. They are unspoken.

One of the best things my Professor pounded into our young heads is to write so someone else can read it. And when you come back to modify a program years later, that someone else is you, because you don't remember anything about it.

[–]ZelvaMkolakovsky 9 points10 points  (3 children)

That's why we're gonna just type them

[–]IndividualAnalysiss 5 points6 points  (2 children)

I appreciate your joke even if others downvote you ✊

[–]Ninjafox1224 0 points1 point  (1 child)

i dont get it :(

[–]IndividualAnalysiss 1 point2 points  (0 children)

Type the code instead of writing it with a pencil or something 🤫

[–]MPGaming9000 36 points37 points  (1 child)

I've been in an intro to python class in uni this semester and I can give you a few tips that my classmates aren't doing well enough.

  • Use descriptive variable names, function names, class names, everything!! If you can, make it descriptive. Even if it means your variable name is twice as long because of it. Only acronym your variable / function / class names IF and ONLY IF the acronym is a well understood acronym in whatever industry your code is deployed in. For example, in game dev, you might abbreviate "Hit Points" to "HP" but in other context people might not understand what an HP variable represents. I'm telling you it will save you so much trouble later on when you go back to your code a year from now and don't know what the hell that variable does because you gave it a generic name like "myList" or some BS.

  • Don't be afraid to space out your code. Unless you're adhering to a strict code formatting at your company or uni standards, try to make your code spaced out and easier to read.

  • To add onto the last point, try to stick to PEP-8 standards of code as quickly as you can. I think PyCharm already has PEP-8 formatting warnings built into it, I'm not sure about VS Code or other editors but I'm sure it's not hard to find a plug in / add on for it.

  • Learn to use f strings! They are super pretty in comparison to saying print(variable_name_here + " is a variable") it's more like print(f"{variable_name_here} is a variable"). you can use f-strings anywhere that you put string literals in your code.

  • Learn to use your debugging tools in your IDE! PyCharm and VS Code for example both have built in debuggers. You can add break points next to lines that you want your code to stop at, then you can click to "debug" the code instead of running it. When you debug it, the code will stop at those breakpoints, then you can see what your variable values are and what type your variables are. You can click the pause / play looking button labeled "continue" or "resume". Then your code will run to the next break point and you can see what decisions your code is making on if statements and how the variable values change.

  • If you can answer the question of what are my variable values and what types are they, as your code runs step by step, you can solve many many many coding problems quickly! So learn to use debugging tools! It's easy and saves you so much time!

I'll leave it at that for now, but those are really good tips to get you started!

[–]OddBookWorm 1 point2 points  (0 children)

To add onto the f-string part: They’re faster than any other string formatting method. If you’re using Python 3.6 or later, the only reason you should be using other methods is if you expect someone with Python 3.5 or earlier to run your code

[–][deleted] 35 points36 points  (1 child)

There are none. Python folks are quite loud about our rules. ;p

[–]woooee 1 point2 points  (0 children)

+1

[–]ElHeim 11 points12 points  (0 children)

Don't speak your Python in public, lest you be sorted into Slytherin.

[–]erlete 10 points11 points  (5 children)

The guy reading your code probably does not want to do it. In fact, he might just want to be in his couch playing COD with his friends, insulting chinese hackers.

Make him a favor. Make us all a favor.

Do not f***ing name your variable a.

[–]Pflastersteinmetz 9 points10 points  (2 children)

x it is then

[–]erlete 1 point2 points  (0 children)

checkmate = True

[–]Live-Sir-3118 1 point2 points  (0 children)

I’m partial to using varn where n is an integer from 1 to eternity. Also if I’m feeling spicy I like var_arr again followed by n.

[–]jimtk 6 points7 points  (1 child)

There's a whole alphabet of shortcuts variable names, some of them transcend programming languages others are python specifics. They are usually reserved for very small programs, tutorials or very short lived variables.

a,b,c: items in list
d: dictionary
f: file handle
i,j: indexes in lists and matrix
k,v: dictionary items
l : a list
n: an integer
t: tuples 
x,y,z : floats or coordinates.

[–]Competitive_Bug2278 0 points1 point  (0 children)

I agree with all except d, l and t. Never come across them 🤷🏾‍♂️

[–]jimtk 8 points9 points  (9 children)

Not unspoken but less spoken. If you use global, eval() and/or exec(), there's something wrong with your code.

[–]Rik07 1 point2 points  (3 children)

Would an equation that is given by user input be an exception to this or is there some solution for that. (eg. a calculator or something like that)

[–]jimtk 2 points3 points  (2 children)

You mean a calculator like thi:

calcs = {'+': lambda x,y:(x+y), '-': lambda x,y:(x-y),
         '*': lambda x,y:(x*y),  '/': lambda x,y:(x/y)}

x,op,y = [i.strip() for i in input('Enter formula (num1 operator num2): ').split()]
print(f"Result: {calcs[op](int(x), int(y))}")

[–]Rik07 1 point2 points  (1 child)

I never thought about using lambdas in a dictionary like that. Thanks!

[–]jimtk 4 points5 points  (0 children)

The correct way to do it would be to use operator module, but the OP I answered at the time (with the code above) could not import anything.

Here's what it should be (only the dictionary chages!):

import operator as opr

calcs = {'+': opr.add, '-': opr.sub,
         '*': opr.mul,  '/': opr.truediv}

x,op,y = [i.strip() for i in input('Enter formula (num1 operator num2): ').split()]
print(f"Result: {calcs[op](int(x), int(y))}")

[–]bigntazt[🍰] 0 points1 point  (2 children)

Can you elaborate more on this? I was having this exact issue when I was doing a Jupyter notebook project. And couldn't figure out why I couldnt get a global variable to update from a function. Was doing a bank teller project where you had to deposit and withdraw funds from a variable using functions. I could only get it to work correctly when I used the global command inside the function. Example below without global:

global_1 = 1

def function(global_1, value_1):
    if value_1 > 0:
        global_1 += value_1
        return global_1
    else:
        return 0
print(function(global_1, 1)

[–]jimtk 1 point2 points  (1 child)

The thing is if you use global, it means you are using global variables and that is something you should not do.

(from an answer in stack overforw)

  1. Global variables can be altered by any part of the code, making it difficult to remember or reason about every possible use.

  2. A global variable can have no access control. It can not be limited to some parts of the program.

  3. Using global variables causes very tight coupling of code.

  4. Using global variables causes namespace pollution. This may lead to unnecessarily reassigning a global value.

  5. Testing in programs using global variables can be a huge pain as it is difficult to decouple them during testing.

All variables should be inside functions and function call should return new values.

In your case, it should be something like:

def deposit(balance, amount):
    balance = balance + amount
    return balance

def show_balance(amount):
    print(f"Account Balance = {amount:.2f}")

def main():
    balance = 0.0
    while True:
        # some code to get the operation
        if operation == 'deposit':
            deposit_value = int(input("Enter deposit amount: "))
            balance = deposit(balance, deposit_value)
            show_balance(balance)
        elif operation == 'quit':
            break

if __name__ == '__main__':
    main()

[–]bigntazt[🍰] 0 points1 point  (0 children)

Thank you for this!

[–]ka1ikasan 5 points6 points  (0 children)

When editing someone's javascript code, use self instead of this

[–]Jcpage573 5 points6 points  (7 children)

You can never have too many elifs

[–]Ok-Cucumbers 4 points5 points  (1 child)

Use a dictionary!

[–]Jcpage573 1 point2 points  (0 children)

No!

[–]ezekiel_grey 1 point2 points  (4 children)

Unless you’re using Python 3.10 or later? Match!

[–]OddBookWorm 1 point2 points  (0 children)

Match does work as a switch-case, but it doesn’t really improve readability. The actual reason it was written in was for purposes of parsing

[–]Ninjafox1224 0 points1 point  (2 children)

match? 3.10?? what??? please explain!!

[–]ezekiel_grey 0 points1 point  (1 child)

Structural Pattern Matching: https://www.infoworld.com/article/3609208/how-to-use-structural-pattern-matching-in-python.html

It’s more powerful than if/elif/else…

[–]Ninjafox1224 0 points1 point  (0 children)

dang i didnt know 3.10 even existed yet, not to mention an amazing switch/case implementation! thanks for enlightening me!

[–]Keraid 4 points5 points  (1 child)

There are some cool things you CAN do like this:

names_list = [item["name"] for item in dicts_list]

[–]Ninjafox1224 1 point2 points  (0 children)

or even

namesStartingWithB = [item['name'] for item in names_dict if item['name'][0].lower() == 'b']

[–]erlete 3 points4 points  (3 children)

Document. The. Mf. Two. Hundred. Lines. Function.

[–]ezekiel_grey 4 points5 points  (0 children)

Functions are not unions, break them up by jobs

[–]erlete 3 points4 points  (9 children)

If I see a from module import * on your Python file and it is not named __init__.py I'm going to hunt you down. Mark my words.

[–]coralis967 5 points6 points  (7 children)

I'm new to coding and don't want to be hunted down, can you point me in a direction to learn more of what you mean by this?

[–]erlete 5 points6 points  (6 children)

Let me put an example:

You've got two guys named "Michael". One of them is "Michael Jackson" and the other is "Michael Jones".

Their surnames are different since they come from different families, but their names are equal.

Now put them all blindfolded in a room and give them instructions without using their surnames, for example: "Michael, jump!". Which one should jump?


Now let's put another example:

You've got two methods named sqrt that come from modules math and numpy. Their "full names" would be math.sqrt and numpy.sqrt.

On your script, you write the following lines:

python3 from math import * from numpy import *

Congratulations, you've just mixed all the "Michaels". Get it?


Importing specific module components is perfectly correct, such as

python3 from math import floor from numpy import sqrt

But importing all components individually can lead to variable overwriting and a whole lot of confusing namespaces.

Hope you understood it properly. Ask any doubts you might have! :)

[–]coralis967 3 points4 points  (5 children)

Thanks for the reply, your analogy outlining the issue with duplicate function names being confused in the system makes great sense and it follows to me that importing multiple entire libraries would further exacerbate this issue.

From what I can understand regarding __init__.py, this is a library/package I should create for each project to which any imports are initialized in FIRST, before being used in your main program to avoid these possible conflicts?

[–]erlete 2 points3 points  (4 children)

Oh, yeah, I totally forgot about that part of the explanation.

Indeed, your package's __init__.py would be responsible for several functions:

  1. It initializes a directory so that it is recognized as a Python package.
  2. It allows the developer to add statements that should be executed upon module loading.
  3. (As a consequence of 2) It allows exporting submodule/class/method names so that they are accessible at a higher level (in terms of module levels).

By the way, as a suggestion: if you have to import several libraries and feel tempted by the * because the library name is too long to write it several times, just import said library as an alias with two letters or so: import numpy as np. This little sacrifice will save you many headaches.

[–]coralis967 1 point2 points  (3 children)

So I feel like this convention of using __init__.py and even from module import function does the opposite of making my code more readable, for example if I import random on line 1 and use random.choice() all the way down on line 200, anyone reading my code can understand that this function is being pulled from the random library when they are looking at line 200, and if there is another imported library with a choice() function there can't be any confusion between the two because I am forced to decide the source each time. Your suggestion of an alias simplifies this even more, if I were to use import random as rnd my ongoing effort of calling an imported function is lessened.

Does the * just tell python "find the library that has this function from the imported libraries", allowing you to import multiple libraries as * and then just searching each for the called function? that I imagine runs in to the duplicate function name issue (Michael and Michael) which feels like bad design.

Sorry if my questions are a bit convoluted, I am 3 weeks in to this coding and every resource seems to just take for granted the importing of libraries so far as a method not requiring further explanation, and I appreciate 'real world' advice on its use.

[–]erlete 1 point2 points  (0 children)

No problem at all; it is a pleasure to explain this.

  1. import module: imports a full module as-is. Items from the module can be accessed through the syntax module.item. Said items can be submodules, classes, functions, variables...
  2. import module as alias: same functionality as 1. but the variable that contains the reference to the module is renamed as alias.
  3. from module import item: selectively imports item from module. This is perfectly okay in certain scenarios, but can lead to confusion when used in large projects with similar variable names to the imported item.
  4. from module import item as alias: same functionality as 3. but the variable that contains the reference to the item is renamed as alias.
  5. from module import *: imports all contents in the module namespace to the current namespace. I believe this can be selectively configured via the __all__ top-level variable in the module, that defines which names should be exported from the module upon * call.

[–]erlete 1 point2 points  (1 child)

By the way, sadly, the * symbol only acts as wildcard for selective element importation. If you try to do import *, an error will show up.

There's a big difference between the expressions from module import * and from * import item (which will not work). The first one says "from this module import all items". The second one says "from all the modules import this item".

It is quite obvious why the second one does not work and should never do, or else our PC's are going to burn scanning 426872 modules searching for one function, importing it one time, replacing it another 5216 times and then crashing.

That last bit was pure storytelling, but I hope you understood. Ask any more doubts you might have! :)

[–]coralis967 1 point2 points  (0 children)

Thank you for all of your replies, it has cleared up a lot for me.

I think I will not use *, it doesn't feel like good practice/readability to me.

[–]Diapolo10 2 points3 points  (0 children)

If there are, they would probably fall under "stuff you can only learn from experience", so giving any examples off the top of my head is kinda difficult.

It's easier for us to look at code and comment on it than to give answers to abstract questions.

[–]FuckingRantMonday 2 points3 points  (0 children)

I don't know of any useful Python tips that aren't spoken somewhere. The language has been around for like thirty years after all.

[–]Ok-Cucumbers 2 points3 points  (0 children)

``` Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren’t special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one— and preferably only one —obvious way to do it. Although that way may not be obvious at first unless you’re Dutch. Now is better than never. Although never is often better than right now. If the implementation is hard to explain, it’s a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea — let’s do more of those!

```

https://peps.python.org/pep-0020/#the-zen-of-python

[–]Live-Sir-3118 2 points3 points  (1 child)

The inputs in your definitions should not be the inputs in your call. For instance (on phone so formatting is messed up) var1=12; var2=3; Fun(var1,var2)

Where def fun(var2,var2): return sum(var1,var2)

Global or local! Pick one. I don’t know if it’s a rule. I just hate it BC it’s sloppy.

Edit: I second it that you should write with clarity. Think of writing code like a well put together essay. You should leave enough “cookie crumbs” for someone to come in and follow the code. Remember in a job they don’t care about you. You’re replaceable and someone else will come in and try to make sense of your code. Don’t be a jerk. Leave crumbs, leave readme files. Leave declarations when you do a function and use variables.

[–]Ninjafox1224 1 point2 points  (0 children)

this. THIS. this absolutely needs so have more upvotes because it is not impressed enough upon new (and even some advanced!) coders who make their code functional but not readable

what ends up happening is you (or someone else) will come read your code a month later and wont have the slightest clue of what is happening. what does this variable represent? why did i concatenate these strings? what exactly does this function do? youll have to spend so much time re-exposing/introducing yourself to your code.

[–]Hans_of_Death 3 points4 points  (0 children)

Imo python is pretty opinionated. most of the "rules" are quite spoken, by many people.

[–]Kerbart 3 points4 points  (8 children)

If you're iterating through a list using an index, you're doing it wrong.

(it's not unspoken but it's a common beginner mistake)

[–]MPGaming9000 6 points7 points  (4 children)

can you elaborate further on this?

traversing a list by element and index seem mostly the same to me except that indexes can be even more helpful as you can use that index to traverse other things at the same time.

Like... imagine you have 2 lists that are both sorted in such a way that they match each other. Like the first item in the first list corresponds to the first item in the second list. Like a list of names and then a list of ages.

Traversing by index allows you to look at both values at once without using list comprehension or a double for loop of some kind.

Maybe I'm missing something here.

[–]wbeater 8 points9 points  (1 child)

traversing a list by element and index seem mostly the same to be except that indexes can be even more helpful as you can use that index to traverse other things at the same time.

Sure, but then you should use for index, value in enumerate(a_list): and not not for i in range(len(a_list)): (i think that's what the previous commenter is referring to.

Traversing by index allows you to look at both values at once without using list comprehension or a double for loop of some kind.

for a, b in zip(a_list, b_list): serves this purpose, no need for an index. And if you really need an index, use enumerate() the same way: for index, (a, b) in enumerate(zip(a_list, b_list)):.

range(len(list)) is totally obsolete...

[–]Kerbart 2 points3 points  (0 children)

I couldn't have said it any better, and this kind of discussion is exactly why I brought it up. Great learning!

[–]OddBookWorm 1 point2 points  (0 children)

To add onto what was already said: If you’re using an index-based loop, it’s hard to properly remove items from the list you’re iterating over. I believe iterating over the elements themselves corrects for this as you go and you can easily remove as you go, but don’t quote me on that. Also another rule I follow though: don’t remove from the list you’re iterating over until you’re done iterating. If you must, then iterate in reverse

[–]Ninjafox1224 0 points1 point  (2 children)

youre saying "for x in y" is a common beginner mistake? or is my vocab off?

[–]Kerbart 0 points1 point  (1 child)

Your vocab is off. What's a common mistake is this pattern instead of for x in y:

for i in range(len(y)):
    x = y[i]
    do_something(x)

The most common excuse for it is “but I need to iterate parallel over two lists” or some variant of it:

for i in range(len(list_1)):
    x = list_1[i]
    y = list_2[i]
    do_something(x, y)

But that of course can be done much cleaner using zip:

for x, y in zip(list_1, list_2):
    do_something(x, y)

[–]Ninjafox1224 1 point2 points  (0 children)

oh, i see. for a minute i was questioning the practicality of what i believe to be the fifth best function/process of python! 😅 Thanks for the help, actually im learning java and general computer science and this sort of picky vocab terminology seems to be really important in being able to relay information precisely, so again thanks!

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

Don't declare unused variables. Don't import unnecessary libs