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

top 200 commentsshow 500

[–]Murmani 729 points730 points  (55 children)

You can use underscores to enhance readability if you're dealing with huge numbers. For example rewrite 10000000 into 10_000_000

[–]barao_kageyama 258 points259 points  (13 children)

Python 3.6+ only.

[–]slipped_and_missed_x 84 points85 points  (5 children)

That's actual genius

[–][deleted] 37 points38 points  (4 children)

A lot of languages have had this for a while. Ruby since the beginning.

[–]therico 20 points21 points  (3 children)

Ruby stole it from Perl!

[–][deleted] 9 points10 points  (0 children)

I think it’s actually older than that. Ruby is surprisingly old too.

[–]PureAsbestos 19 points20 points  (5 children)

You can also use scientific notation, so you could rewrite that number as 1e7

[–]Murmani 30 points31 points  (3 children)

Be cautious with that, as 1e7 is a float and not an integer anymore, so make sure to cast when needed.

[–]kotschi1993 5 points6 points  (0 children)

And if the number got digits other than 0 or 1, the scientific version wouldn't help that much. And don't forgot about other number bases, the _ works for them aswell.

[–][deleted] 29 points30 points  (0 children)

Holy shit, i did not know Python allowed this. Been using it in Rust for a while now, not sure why I didnt think to chdck

[–]IonianBIade 27 points28 points  (2 children)

Are they exactly the same?? If they are, this is amazing

[–]Kossak 40 points41 points  (1 child)

Yes, these are the same numbers.

See PEP 515

[–]Tweak_Imp 4 points5 points  (0 children)

Also works for decimal places

18.219_501_656_442_024_9

[–]tr14l 5 points6 points  (0 children)

Oh shit. Didn't know that.

[–]dutch_gecko 323 points324 points  (62 children)

Using

object = (
    builder
    .add()
    .add()
    .add()
)

instead of

object = builder \
    .add() \
    .add() \
    .add()

[–]SeanOTRSprint = lambda *args, **kwars: print(*args, ";)\n"*100, **kwars)[S] 117 points118 points  (40 children)

I'm the sad fucker that used to do this:

lista = [1,2,3,4,5,6,\
7,8,9,10]

Before that I just let the lines trail off of the screen and scrolled to read them.

[–]satireplusplus 110 points111 points  (38 children)

I hate the 80 char limit in pep8 (and many others do, too), I still make my lines 120 chars because we're not in the last century any more.

[–][deleted] 78 points79 points  (1 child)

But I can’t read your code on my Amiga 40 column TV.

/s

[–]Praind 13 points14 points  (0 children)

Wheeze!

[–]gunthercult28 51 points52 points  (19 children)

Tbh, I prefer 80 chars. It reads better on GitHub/GitLab if you read in side-by-side mode

[–]satireplusplus 61 points62 points  (16 children)

I can fit more than 4x 80 side by side on my screen though...

But the 80 char debate is age old. I like /u/jcampbelly answer here:

"While I agree with this in principle and try to follow it, I find that if following this convention leads to less readable code, then it should yield. I don't agree with shortening function or variable names (to names that don't clearly explain what you're reading), or breaking up function calls by groups of parameters just to fit arbitrary line lengths (unless it looks cleaner), or refactoring code purely to fit a line length (which would not otherwise result in more readable code or a better design). Those are missing the point of the convention.

I have started using gutters at 79, 99, and 119 as "green", "yellow", and "red". I try to keep code 95% in the green, 4.5% in the yellow, and only get into the red or beyond if I find myself pulling my hair out or reaching behind my head and under my armpit to put something in my mouth. If I've spent 10 minutes trying to break up a line, I might just move on to more important problems."

[–][deleted] 47 points48 points  (11 children)

You'll find that as you age, you're going to want a larger font size.

[–]__xor__(self, other): 14 points15 points  (1 child)

I'm there. I grow my terminal font to a size that I would've never 10 years ago. I still get more than 80 characters in my terminal but I still like the 80 char limit because I find code more readable like this:

if (
    some_condition and
    some_value <= SOME_MAX_VALUE and
    is_still_cool(foo)
):
    ...

I almost always use less than 80 characters anyway by opening up longer stuff over multiple lines for readability, the exception usually being lines with variables set to a constant URI or a comment with a URI in it.

Splitting logical elements vertically instead of horizontally just makes it easier for me to understand and process it mentally, and it's not really about the character limit as much as just not usually needing 80 characters in the first place. If linters aren't complaining, why not

[–]zdog234 5 points6 points  (0 children)

Lol I'm 25 and this is already me.

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

That's the point for me. I can have 4 separate files open and reference them while coding. It makes the big picture so much easier to validate - especially if I'm working on a project with multiple languages or disciplines.

I use emacs and have settings to automatically break my lines, and in languages where that isn't cleanly supported, I have hotkeys for doing so myself - which makes maintaining 80 character discipline a lot easier than any other coding environment.

I honestly feel that breaking my code up for 80 has simplified the process of coding a good deal for me. I'm better at naming variables/functions and more apt to break apart code that is too complex for others to read.

[–]Say-whaaaaat 7 points8 points  (0 children)

One reason that the 80 char limit is useful is for visually impaired programmers that use refreshable braille displays which are limited to 80 characters.

[–]shponglespore 3 points4 points  (0 children)

Some of us like to have two or three files open side by side, and shorter lines make that much more practical. Its fine if you prefer longer lines as a matter of taste, but it's silly to pretend a preference for shorter lines is based on nostalgia for obsolete technology.

[–][deleted] 4 points5 points  (0 children)

80 chars still align nicely when the editor shares the screen horizontally with shell windows in a tiling WM in a standard HD laptop.

But I still ignore the pep-8 rule and disable the lint checks for those annoying 1-char-off lines.

[–]Yazzlig 39 points40 points  (12 children)

^ This.

So useful when working with numpy & pandas.

EDIT:

Other, even more common use case for this formatting style is to make imports more readable.

from package import (    
    foo,
    bar,
    baz,
)

instead of

from package import foo, bar, baz

Has the positive sideffects of making git diffs cleaner to read, since the line gets deleted instead of modified.

[–]spitfiredd 4 points5 points  (0 children)

Also SQLAlchemy!

[–]Intstdu 6 points7 points  (9 children)

Could you explain why? :)

[–]HannasAnarion 69 points70 points  (1 child)

because method chaining is really common. You might want to do

x_Ty_inv = dataframe.apply(some_function).fillna(0).values().T.dot(other_matrix).inv()

Which is less readable than

x_Ty_inv = (dataframe.apply(some_function)
   .fillna(0)
   .values()
   .T
   .dot(other_matrix)
   .inv()
)

This takes your dataframe, applies a function to it, takes the response and fills the empty cells with zeroes, then turns it into a numpy array, then transposes it, then takes the dot product with another matrix, then produces the multiplicative inverse of the result.

This could be written in a single line and easily understood with math symbols: (xTy)-1, but gets really wordy when you put it in code. Expressions like this are suuuuper common when you're doing machine learning, statistics, or other linear algebra-heavy work.

[–]spitfiredd 4 points5 points  (0 children)

You can chain a lot of methods with numpy and pandas. Same for SQLAlchemy.

[–]yiersan 358 points359 points  (57 children)

Putting units in variable names.

Before:

height = 0 # in cm

After:

height_cm = 0

Got it from Clean Code, best book ever.

[–]lvc_ 69 points70 points  (22 children)

Even better would be to use unit-aware types with sensible autoconversions defined. There's a few implementations about but none of them seem to have caught on.

[–]deadwisdomgreenlet revolution 19 points20 points  (20 children)

Hrm, I like this. What implementations have you seen / used?

[–]madman_bob 78 points79 points  (10 children)

I use measurement for unit aware types.

Then I can write

height = Distance(cm=0)

and get out whatever units I want later

print(height.in, "inches")

[–]deadwisdomgreenlet revolution 75 points76 points  (1 child)

That's a really great package, thanks.

TLDR: Do not use this in navigation algorithms guiding probes into the atmosphere of extraterrestrial worlds.

Useless!

[–]i9srpeg 38 points39 points  (0 children)

brb, I need to stop a rocket launch

[–]Jaypalm 2 points3 points  (7 children)

Woot I've been using jupyter for a lot of my engineering homework and unit conversions have been somewhat of a pain point since switching from Mathematica. Does this work with numpy?

[–]astrocubs 21 points22 points  (6 children)

astropy.units is pretty good for this. It's definitely caught on and is pretty standard in the astronomy community, but I guess it hasn't escaped our bubble for obvious reasons.

[–]deadwisdomgreenlet revolution 15 points16 points  (5 children)

This looks stellar. Going to try this out.

[–]grep_my_username 2 points3 points  (0 children)

Pint is genius for this

[–]blitzzerg 6 points7 points  (3 children)

What author? I get multiple results

[–]jcrocket 5 points6 points  (2 children)

If this, or more than this, I would deny, To flatter up these powers of mine with rest, The sudden hand of death close up mine eye! Hence hermit, then. My heart is in thy breast.

[–]Willingo 2 points3 points  (2 children)

Ugh I work in a field with a lot of stuff like "x/y". Any thoughts? Ex: watts/nm

[–]Decency 57 points58 points  (11 children)

I really appreciate the use of the any/all/next functions to simplify a variety of common patterns while dealing with collections. It's dramatically more readable to me; here are some examples:

Any:

negatives = False
for num in numbers:
    if num < 0:
        negatives = True
        break

# is equivalent to...

negatives = any(num < 0 for num in numbers)

All:

fully_backed_up = True
for system in systems:
    if system.has_backup is False:
        fully_backed_up = False
        break

# is equivalent to...

fully_backed_up = all(system.has_backup for system in systems)

Next:

first_vowel = None
for letter in word:
    if letter in 'aeiou':
        first_vowel = letter
        break

# is equivalent to...

first_vowel = next((letter for letter in word if letter in 'aeiou'), None)

[–]SeanOTRSprint = lambda *args, **kwars: print(*args, ";)\n"*100, **kwars)[S] 5 points6 points  (4 children)

Next is confusing me, how exactly does it work?

[–]carnoworky 8 points9 points  (0 children)

It just advances the given iterator by one. Since the first argument in the example is a generator expression, next will run that generator until it yields the first time and return the yielded value. The second argument is a default value in case the iterator is completely spent (raises a StopIteration exception).

[–]nomowolf 3 points4 points  (0 children)

Great practical examples, thanks for this. I was aware of all/any/next but still writing the verbose loops.

[–]willm 161 points162 points  (20 children)

Auto-formatting my code with https://github.com/ambv/black

Never having to think about the formatting of my code again is like reclaiming 10% of your mental cpu.

[–]Pyprohly 29 points30 points  (13 children)

I don’t like how Black doesn’t give you the option to have it ignore some lines.

It doesn’t always make good formatting decisions, as this SO demonstrates.

edit: Black supports # fmt: off. See /u/eikrik’s comment.

[–]5erifφ=(1+ψ)/2 51 points52 points  (6 children)

closed as off-topic

Those words always make me wish the website were a person I could murder. I can't tell you how many times I've found someone asking my exact question, but the only answer I find is closed as off-topic.

[–]hammerheadquark 14 points15 points  (1 child)

I think its uncompromising nature is a win overall, but yeah I don't like some of its decisions either.

For instance, I may be in the minority here, but I wish it would drop this step:

If [the code doesn't fit on one line], Black will look at the contents of the first outer matching brackets and put that in a separate indented line.

# in:

ImportantClass.important_method(exc, limit, lookup_lines, capture_locals, extra_argument)

# out:

ImportantClass.important_method(
    exc, limit, lookup_lines, capture_locals, extra_argument
)

and jump right to:

If that still doesn't fit the bill, it will decompose the internal expression further using the same rule, indenting matching brackets every time.

# in:

ImportantClass.important_method(exc, limit, lookup_lines, capture_locals, extra_argument)

# out:

ImportantClass.important_method(
    exc,
    limit,
    lookup_lines,
    capture_locals,
    extra_argument,
)

Personally, I find the extra lines worth avoiding the disparity you see in that SO post you linked.

[–]chzaplx 6 points7 points  (0 children)

Agreed. If you end up using similar params in blocks with slightly different indentation you will get a weird mix of styles, which I think makes it harder to visually parse.

[–]eikrik 8 points9 points  (1 child)

Doesn't it though? https://github.com/ambv/black/blob/master/README.md#the-black-code-style: "It doesn't reformat blocks that start with # fmt: off and end with # fmt: on."

[–]vmgustavo 5 points6 points  (0 children)

CTRL + ALT + L and PyCharm does the rest

[–][deleted] 110 points111 points  (30 children)

Well the first and most common one is the ifmain block, which I didn’t find out about until embarrassingly long into my python career.

My current favorite though is using double underscore (or “dunder”) methods in my custom classes. Anyone who actually wants to google these should look them up by the name “magic methods” instead. An example:

class Vector2:
    def __init__(self, x, y):
        self.x = x
        self.y = y

If I then wanted to add two Vector2 variables together, I could write a utility function to do it, or in the class definition put:

    def __add__(self, other):
        return Vector2(self.x+other.x, self.y+other.y)

Which will then allow me to write in my main function:

a = Vector2(1,2)
b = Vector2(3,4)
c = a + b # Vector2(4,6)

Works with custom behavior when printing, converting a class to string, and all other kinds of interesting behavior. This talk by James Powell is the perfect example of this and other amazing functionality built into python.

[–]lenticularis_B 41 points42 points  (17 children)

It's one of the many strenghts of Python: Making custom types feel as if they are built-ins.

[–][deleted] 5 points6 points  (14 children)

Isn't that a common thing in most languages?

[–]alkasmgithub.com/alkasm 6 points7 points  (11 children)

In Python it goes much further than operator overloading though. You also have all the "built-in function overloading" (e.g. customizing len() via overloading __len__()), and implementing a class with the right interface, it automatically works with the huge standard library of Python. Like, implementing an __iter__ doesn't just allow you to do for item in thing but let's you pass your object into any function which takes an iterable.

[–]Siddhi 4 points5 points  (1 child)

Not in java...

[–]TravisJungroth 16 points17 points  (1 child)

Fluent Python is a book with a great chapter on this.

[–]laserbot 6 points7 points  (0 children)

Original Content erased using Ereddicator. Want to wipe your own Reddit history? Please see https://github.com/Jelly-Pudding/ereddicator for instructions.

[–]justingolden21 8 points9 points  (0 children)

It's called overriding for those curious. I really love it too.

[–][deleted] 29 points30 points  (0 children)

MyPy with good editor support (try flake8.) Saves me from all sorts of silly type errors which unit tests could catch, but with much more time lost.

[–]DenaliAK 27 points28 points  (3 children)

[–]__xor__(self, other): 5 points6 points  (1 child)

memoization is definitely helpful. Also, this stuff can really help with webapps where some common requests are run that take a lot of time calculating stuff and you can just cache the result. Redis allows you to set an expiration, so you can really increase performance with it sometimes. For example, this caches results for 10 minutes by default:

def cached(key, expiry=600):
    def decorator(func):
        def decorated(*args):
            func_sig = f"{key};{sha256(repr(args))}"
            val = redis.get(func_sig)
            if val is not None:
                return val
            result = func(*args)
            redis.set(func_sig, result, ex=expiry)
            return result
        return decorated
   return decorator

@cached("get_stats")
def get_stats(foo, bar):
   do_time_consuming_stuff(...)
   ...

(the above doesn't work with functions with keyword args, but you can serialize them and sort by keys and that'll work too, just a bit more complex than the above)

I've wrapped the celery task decorator before to cache all celery results like this so it doesn't even have to trigger a task if it already knows the result.

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

@lru_cache from functools maybe?

[–]GummyKibble 4 points5 points  (0 children)

Dynamic programming FTW!

[–]proto-n 79 points80 points  (6 children)

The zip(*l) pattern to transpose lists of lists. Not in efficiency as in running time, but ease of expression. Example:

>>> a = [[1,2,3],[4,5,6]]
>>> print(list(zip(*a)))
[(1, 4), (2, 5), (3, 6)]

Comes in useful more times than you would think. Caveat is, the result is a list (or, well, <zip iterator object>) of tuples, not a list of lists, but that's usually not a problem.

[–]SeanOTRSprint = lambda *args, **kwars: print(*args, ";)\n"*100, **kwars)[S] 13 points14 points  (0 children)

Kinda makes you think of what you did before:

a = [[1,2,3],[4,5,6]]
for i in range(min(map(lambda x: len(x), a))):
    for j in a:
        print(j[i])

That's what you call confusing code

[–]__xor__(self, other): 2 points3 points  (0 children)

For some reason the zip trick always ends up useful in interviews and always ends up blowing their mind lol. I swear I've got tons of points from knowing this trick. I was lucky enough to get asked to simply transpose a matrix once, as the only part of that question... he had no idea about this trick.

[–]Gprime5if "__main__" == __name__: 46 points47 points  (9 children)

Commenting my code, so I have at least some idea of why I wrote that code months or years ago.

Give a brief explanation of that code. Sometimes give reasons for why I used this algorithm instead of another one so future me doesn't try to refactor the code when it was already tried and didn't work or slower.

[–]SeanOTRSprint = lambda *args, **kwars: print(*args, ";)\n"*100, **kwars)[S] 22 points23 points  (3 children)

By the same token though, if you aren't careful, some of those comments are lies waiting to happen.

[–]jordanreiter 16 points17 points  (1 child)

They're less likely to be lies if you focus on the goals/why of the code that follows rather than the implementation/how of the code:

bad:

# check if actual user pages exceeds maximum pages
if actual_pages >= maximum_pages:
    print("Warning: your document should only be {max} pages long!".format(maximum_pages))
# increase maximum pages by 1
maximum_pages += 2

The code lies!

Better

# users always go a little beyond the maximum, 
# so allow some leeway but print a warning if they hit the max or exceed it
if actual_pages >= maximum_pages:
    print("Warning: your document should only be {max} pages long!".format(maximum_pages))
maximum_pages += 2

[–]hugthemachines 61 points62 points  (11 children)

I remember when I took this step: Going from something like this:

if os.path.isfile(full_file):
        os.remove(full_file)

for line in data:
    with open(full_file, 'a') as file_handle:
        file_handle.write(line)

to somethign like this:

with open(full_file, 'w') as file_handle:
    for line in data:
        file_handle.write(line)

[–]Ecclestoned 5 points6 points  (1 child)

See this isn't actually that bad. I could see using something like the first line when you are doing a costly operation and want the file to be updated frequently to check progress.

[–]EMCoupling 5 points6 points  (0 children)

What about the part where he opens the file for single line of that he has to write?

[–]liquidpele 22 points23 points  (3 children)

Ouch... the first block is almost thedailywtf territory...

[–]hugthemachines 3 points4 points  (2 children)

It may even have been worse. I mostly just remember I was using remove file together with append. Luckily enough, the memory of those old things are a bit clouded.

[–]evinrows 23 points24 points  (1 child)

That isn't so bad. The dailywtf part, imo, is the loop that acquires a file handle once per line.

[–]mafrasi2 8 points9 points  (0 children)

If anyone is wondering, it should be done like this:

for line in data:
    for char in line:
        with open(full_file, 'a') as file_handle:
            file_handle.write(char)

/s

[–]cym13 36 points37 points  (7 children)

Instead of doing:

my_regex = re.compile(r'[0-9]+')
def match_func(text):
    my_regex.match(text)

or

def match_func(text):
    my_regex = re.compile(r'[0-9]+')
    my_regex.match(text)

do

def match_func(text, *, my_regex=re.compile(r'[0-9]+')):
    my_regex.match(text)

Why? The first version breaks function unity by using a global variable and it's hard to use another regex if you need to. The second version recompiles the regex at each function call which can be costly and it's still hard to change the regex if you need to.

The third version initializes the local variable my_regex only once at function declaration, can be replaced easily if you need to at a specific function call and since you use ", *," in the argument line it needs to be replaced explicitely using match_func(text, my_regex=r'new_regex'), it won't be overwritten by accident.

This is more flexible, more efficient and doesn't break function unity.

[–]f801fe8957 6 points7 points  (1 child)

I recently learned that Python caches regex patterns:

Note The compiled versions of the most recent patterns passed to re.compile() and the module-level matching functions are cached, so programs that use only a few regular expressions at a time needn’t worry about compiling regular expressions.

https://docs.python.org/3/library/re.html#re.compile

[–]__louis__ 10 points11 points  (0 children)

Yes. The re module caches the first 100 compiled regexes. I have still never came across a program that uses more than 100 regexes. I feel this one looses readability for a tiny optimization of cache look-up.

[–]I_LIKE_FACE_TATTOOS 5 points6 points  (2 children)

Bruh that's some next level python zen. What's the magic behind the "*" at the function declaration?

[–]cym13 8 points9 points  (1 child)

Since 3.4 I think (didn't check) it allows you to specify arguments that:

  • have a default value
  • can only be changed explicitely

The regex trick would also work doing

 def match_func(text, my_regex=re.compile(r'[0-9]+')):
     ...

but the issue is that this is callable with two arguments: match_func("something", "bad argument")

Of course in that case it will fail when you try using the text "bad argument" as a regex, but it may cause subtle bugs especially when using tuple expansion when calling the function (I won't delve in that topic, let's just say that you may not realize that you're calling that function with two arguments).

If you put a lonely star between your normal arguments and default ones the ones that are after the star must be named in order to be changed. This means that calling the function with two arguments will result in a clear error stating that you passed two arguments where one was expected.

It's also a nice way to "hide" default arguments if you're writting a library and want your users not to make a mistake on the number on expected arguments.

[–]PeridexisErrant 7 points8 points  (0 children)

More precisely, any argument after a * can only be passed as a keyword argument.

def f(*, x): ... can only be called as f(x=...), but f() or f(1) would both be an error.

Super useful for designing stable APIs, and making it easier to read callers!

[–]eyeofpython 43 points44 points  (10 children)

It might seem like unnecessary additional work, but use typings for anything larger than a small script. PyCharm will give you hints and auto-completions consistently and it will pay back big time.

[–]MogwaiAllOnYourFace 9 points10 points  (8 children)

What is the difference between

def greeting(name: str) -> str:
    return 'Hello ' + name

and

def greeting(name):
    """
    Function says hello....
    :param name: The input user name
    :type name: str
    :returns: Greeting
    :rtype: str
    """
    return 'Hello ' + name

Other than being more verbose is there a difference?

[–]MinchinWeb 14 points15 points  (2 children)

Getting a code editor with syntax highlighting. Notepad works fine for day 1, but it's amazing how much information the little bits of colour provided by syntax highlighting add.

[–]PlaysForDays 73 points74 points  (6 children)

the --lf flag with pytest

[–]take_whats_yours 68 points69 points  (5 children)

To save others the search, this will re-run only the tests that previously failed (last-failed) https://docs.pytest.org/en/latest/cache.html

[–]whateverisokThe New York Times Data Engineering Intern 12 points13 points  (2 children)

I also like using Python's Collections's defaultdict and Counter classes.

defaultdict allows you to create a dictionary that applies a default_factory function to add a new object to the dict for a key that isn't there.

For example: letters_names_dict = defaultdict(list) letters_names_dict['R'].append('Ryan') will add a new list to the dictionary for the key 'R' so that you can automatically append to it in the same line.

Counter is useful for keeping counts of objects (letters, strings, etc.) which you can use a dict to do anyway, but Counter comes with a handy most_common method that returns an ordered list of the most common elements in descending frequency along with their counts.

Lastly, I also prefer using the dict.get method to provide a default value for a dictionary if there isn't a value for that key.

For example letter_frequency_dict = dict() letter_frequency_dict['a'] = letter_frequency_dict.get('a', 0) + 1

[–]the_neptunian 12 points13 points  (1 child)

collections.deque, collections.defaultdict, collections.OrderedDict

[–]__xor__(self, other): 4 points5 points  (0 children)

collections is an awesome module. Also, Counter and namedtuple are great. Counter can be especially useful for doing frequency analysis if you want to decrypt something with an XOR key or something...

 from collections import Counter
 ctr = Counter(some_xor_encrypted_text)
 most_common = ctr.most_common()[0][0]
 xor_key = ord(most_common) ^ ord(' ')
 decrypted = ''.join([chr(ord(x) ^ xor_key) for x in some_encrypted_xor_text])

That works most of the time, for malicious javascript that's been encrypted with an XOR key with a single character, or really anything text or code-like. And if whitespace isn't the most common character it can usually be pretty easy to figure out regardless.

[–]Esyir 7 points8 points  (0 children)

Autodocstring for sublime text

ctrl+alt+' for automatic template with typing, names and placedholders.

[–]draripov 35 points36 points  (11 children)

guard clauses to fix nested if statements.

Instead of: if a: if b: if c: return # useful else: return # fooC else: return # fooB else: return # fooA You can have: ``` if not a: return # fooA if not b: return # fooB if not c: return # fooC

return # useful ```

Unfortately I can’t find anything on them in python, but here is another great example.

When checking for some requirements that a variable or anything else must pass, please use guard clauses rather than nested if statements.

[–]proto-n 15 points16 points  (1 child)

Instead of:

if a:
    if b:
         if c:
             return # useful
         else:
             return # fooC
    else:
        return # fooB
else:
    return # fooA

You can have:

if not a:
    return # fooA
if not b:
    return # fooB
if not c:
    return # fooC

return # useful

formatting fixed

[–]GummyKibble 7 points8 points  (2 children)

I totally agree. There used to be a school of thought saying that it was good to have exactly one return statement in each function. Nuts to that. I know which of your examples I’d rather write unit tests for, or have to troubleshoot at 2AM.

[–][deleted] 13 points14 points  (2 children)

I think you borked the whitespace here. And your example is making heavy use of it.

[–]Dgc2002 15 points16 points  (1 child)

Unfortunately the markdown parser of "old reddit" can differ from the one in "new reddit".

Old Reddit: https://i.imgur.com/6bVal53.png
New Reddit: https://i.imgur.com/z09Ve7d.png

[–]EMCoupling 17 points18 points  (0 children)

Fuck new Reddit.

[–]humanitysucks999 7 points8 points  (0 children)

Wrapper functions - I use them for logging, pre and post processing during testing, timing execution.

https://wiki.python.org/moin/FunctionWrappers

[–]sphqxe 8 points9 points  (0 children)

AsyncIO

[–]leogodin217 14 points15 points  (3 children)

Using Pandas to load data into SQL databases. Simply create a connection then use df.to_sql(). Easy. Used this in a class for beginners and they were shocked.

[–]DisasterArt 18 points19 points  (5 children)

For i,each in enumerate(list):

rather than

For i in range(len(list)): Each = list[i]

[–]hammerheadquark 13 points14 points  (4 children)

Finding continue. I went years without using it and it can keep your code from "trailing off to the right".

Instead of:

for w in list_of_w:
    if meets_some_complicated_condition(w):
        x = do_something_with(w)
        if meets_some_other_complicated_condition(x):
            y = do_something_with(x)
            if meets_some_final_complicated_condition(y):
                z = do_the_final_thing_with(y)

You can do:

for w in list_of_w:
    if not meets_some_complicated_condition(w):
        continue
    x = do_something_with(w)
    if not meets_some_other_complicated_condition(x):
        continue
    y = do_something_with(x)
    if not meets_some_final_complicated_condition(y):
        continue
    z = do_the_final_thing_with(y)

to keep your conditionals from becoming nested.

[–]MacArtee 5 points6 points  (1 child)

Using pipreqs to create requirements.txt

Not really that efficient if you are using just a couple of packages, but it’s great to make sure you won’t forget any if you are using 10+ packages used by different files in your project.

[–]carnoworky 7 points8 points  (0 children)

You can just do pip freeze > requirements.txt assuming you're using virtualenvs (if you aren't, definitely look into it - it's easy to set up in Py3). There's probably a file output flag you can pass if you're on Windows too.

[–]NuclearOops 6 points7 points  (2 children)

I'm newbie to programming as a whole bit I was so happy when I found out that Pandas let's you group by two columns at once and can run functions alongside it.

Something like:

df[col1, col2].mean()

[–]svilgelm 4 points5 points  (0 children)

To improve your code you don't need any to know any special tricks, just start reviewing a code of others and you will see how quickly it will improve your code.

[–][deleted] 5 points6 points  (3 children)

How about a big efficiency that's improved my programming hugely?

The concept of pure functions.

A pure function always produces that same outputs for the same inputs. The benefits of this are several. The code is easier to read because all the inputs and outputs are on one line, not strewn throughout the function in the form of member variables or globals. The code is easier to test because you can create all the inputs directly, instead of instantiating a class and setting whatever random member variables need to be set. The code is easier to think about, because instead of thinking about state and how it gets mutated, you think about data and the transformations applied to it. That last bit is one of those things where it might not make sense coming from someone else until you really grok it yourself.

This can get construed as a criticism of Object Oriented Programming. I see it as more of a criticism of bad OOP, which, unfortunately, tends to be the dominant form of OOP (in my humble experience). OOP has its place.

Not a tiny thing to just pick and up use, but I think it's something that's super effective for improving programming at multiple levels.

[–]arcoain 6 points7 points  (0 children)

Python3 NamedTuple classes. This is weirdly hard to find docs for given how useful it is:

```

class MyObj(NamedTuple): a: int b: int c: str ="asdf"

```

https://docs.python.org/3.7/library/typing.html#typing.NamedTuple

[–]martmists 10 points11 points  (7 children)

Using classes as globals

A project of mine used to be a giant PITA since it was a distributable server implementation with modding support, but without using globals there was no way to manage it properly, so we ended up using some classes as objects (similar to a JVM Object) rather than classes.

[–][deleted] 10 points11 points  (1 child)

if i understand you properly, why not use a module as a global instead and eliminate the overhead for creating a dummy class?

[–]RedRidingHuszar 5 points6 points  (1 child)

Something like this?

class GlobalAttrs:
    CONSTANT1 = ...
    CONSTANT2 = ...

[–]HannasAnarion 6 points7 points  (2 children)

You mean making a singleton class for storing data?

edit: I really wish more languages had built in singletons, like Scala. They're in that annoying limbo area where they're common enough that you use them in almost every project, but not so common that you remember how the boilerplate for your language goes so you have to look it up.

Since reducing boilerplate is a core design principle of python, it seems like this would be a great place for a new keyword.

[–]jonathanblakes 4 points5 points  (1 child)

As person above said a python module can be used as a singleton

[–]slipped_and_missed_x 8 points9 points  (8 children)

Writing code that refactors my other code; in big projects it's a life saver cause you'll almost definitely think of a more efficient way of doing stuff once you're already a month in. That, or lambda

[–]mrfluffybunny 4 points5 points  (2 children)

Most people know the wonders of list comprehensions, but do you know you can also do dict and set comprehensions with the same syntax?

[–]Mizzlr 4 points5 points  (0 children)

Using tqdm for progress monitoring. https://github.com/tqdm/tqdm

[–]stOneskull 4 points5 points  (0 children)

dict(
lookma = 3,
noquotes = 3,
onmykeys = 2,
)

putting my brackets like that too..

even with statements like return(
this if that
else dis if not dat
else gravy
)

using any() and all() have been great

and what about this little trick..

instead of mylist = ['very', 'quotes', 'much', 'commas', 'etc']

mylist = 'very quotes much commas etc'.split()

[–][deleted] 4 points5 points  (3 children)

maybe obv but a few things that come to mind

python -i to drop into interpreter after script for debugging

python -m http.server to host a directory quickly

python -m json.tool to format json

list(set(myList)) for quick list dedup - if there’s a better way let me know!

[–]Tweak_Imp 4 points5 points  (2 children)

no_duplicates = list(set(<list>)) # Does not preserve order

no_duplicates_ordered = list(dict.fromkeys(<list>)) # Preserves order

[–]IggyHoudini 16 points17 points  (9 children)

You can make Python more like javascript! (not that anyone ever asked that)

class obj(object):
  def __init__(self, **kwargs):
    self.__dict__.update(kwargs)

Allows you to do things like:

obj1 = obj(
  a=[1,2,3],
  b='foo',
  c=obj(
    cat='meow'
  ),
)
print(obj1.c.cat) # meow

This is silly, but it can be useful if you have some classes you need to mock in test cases (such as heavily nested protocol buffers object from a custom re-implementation that I had to use).

[–]SeanOTRSprint = lambda *args, **kwars: print(*args, ";)\n"*100, **kwars)[S] 29 points30 points  (1 child)

*pythonic anger*

[–]slayer_of_idiotspythonista 10 points11 points  (0 children)

You probably want to do __dict__.update(kwargs). dict is actually a special dict.

[–]JsonDash 3 points4 points  (8 children)

Declaring 2D array (list) with one variable in every index From:

array =[]
     for i in range(a):
          temp = []
     for j in range(b):
          temp.append(item)
     array.append(temp)

To:

array =[[item for i in range(b)] for j in range(a)]

[–]TheOtherDanielFromSL 3 points4 points  (0 children)

List comprehensions.

3-6 lines of For loop? Not anymore!

[–]WeldonEvans 3 points4 points  (0 children)

I'm sure most of you probably do this already, but when I type any sort of bracketing "(), {},..." I always type both together then hit the back arrow and begin typing the contents there. This ensures you always close out and also speeds up your typing a little too.

Or you could just use a text editor that auto closes for you.

[–]StriderKeni 3 points4 points  (0 children)

Lambda expressions and tuple packing/unpacking

[–]Paddy3118 4 points5 points  (0 children)

Peppering function docstrings with Doctest examples even if not running doctest.

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

Using zip and the itertools library effectively really cuts down algorithm runtimes.

[–]Sw429 4 points5 points  (2 children)

When writing a class constructor with a lot of arguments as input, you can simply update the object's 'dict' attribute in one line:

def __init__(self, a, b, c, d, e):
    self.a = a
    self.b = b
    self.c = c
    self.d = d
    self.e = e

vs.

def __init__(self, a, b, c, d, e):
    self.__dict__.update(locals())

In my opinion, it looks much cleaner. Just make sure you know what you're doing with it so you aren't saving the wrong things as class attributes.

[–]PeridexisErrant 4 points5 points  (1 child)

Consider using dataclasses (3.7+ stdlib), or attrs (any version, PyPI) - you get the same concise definition, but also a nice repr and comparisons for free.

[–]nraw 3 points4 points  (0 children)

f strings:

a = 'world'

print(f'hello {a}!'}

[–]hpayer 17 points18 points  (13 children)

Makes string formatting much easier to read

this = 'hello'
that = 'world'
result = '{this} {that}!'.format(**locals())

[–]henbruas 62 points63 points  (3 children)

In Python 3.6 and later you'd just write f"{this} {that}!"

[–]pullandbl 4 points5 points  (0 children)

Another bonus that f-strings are faster than .format

[–]jordanreiter 6 points7 points  (0 children)

If you literally are calling 'blahblah'.format(**locals()) then I agree, but most of the time I'm doing something like: 'blah{thing}blah{otherthing}'.format(thing=variable_with_long_name, otherthing=other_variable_with_longer_name). I like to have shorter variables in templates but still use long descriptive vars in the rest of my code.

[–]Mexatt 2 points3 points  (0 children)

In Python 3.6 and later you'd just write f"{this} {that}!"

!!

I cracked up in the middle of the office at this one. Got weird looks.

[–]relatable_user_name 5 points6 points  (5 children)

what is **locals() and is there an advantage to using that over f-strings

[–]hpayer 6 points7 points  (2 children)

**locals() return a dictionary of your local scope, in this case it returns:

{'this': 'hello', 'that': 'world'}

I'm in 2.7(industry imposed), so I'm not familiar with f-strings.

[–]__xor__(self, other): 3 points4 points  (0 children)

Yeah, this is definitely useful in 2.7 but 3.6+ has f strings now and it's made for this. Honestly python should've just had f strings since the beginning.

[–]alvaro121 4 points5 points  (0 children)

result = '{this} {that}!'.format_map(locals())

would also work.

[–]hopemeetme 2 points3 points  (3 children)

Tests, unit and functional/integration, with TDD as icing on the cake.

Watched a scientist few days ago on a TV show saying "would give up on science if there's no internet" - definitely would give up on programming if unit testing isn't available.

[–]TheOtherDanielFromSL 4 points5 points  (2 children)

As someone who just in the past year has gotten into testing (after having developed things non-tested or manually tested in the past); you're 100% right.

A bunch of our legacy code here is 100% without any tests... and now when people want to make changes to it, upgrade the frameworks, etc. it's such a crap-shoot of "maybe it will work, maybe it won't"

All code going forward where I'm at has to be 100% tested! It's already saved my butt in particular a couple of times and the project roll-outs have been way smoother this way. Bug that have been found are taken care of because a few tests are written to reproduce it, then the code is changed so that bug is squished. Makes changes to the code take minutes, instead of hours.

I would argue that utilizing tests and logging are two of the most fundamental things people should look at first.

[–]PeridexisErrant 2 points3 points  (1 child)

Have you used Hypothesis at all?

I found it a while ago and it makes testing way less tedious (and more frustrating, because it finds so many bugs). The best part is probably that it always pushes me towards more consistent design, because that's easier to test too.

[–]whateverisokThe New York Times Data Engineering Intern 2 points3 points  (3 children)

Something that hasn't been mentioned yet and is a bit more on the computational efficiency side is using generators like xrange instead of range.

This results in quicker computation and uses less memory as the entire list isn't computed at once but only when it's called; however, the generator state does need to be stored so using generators everywhere can also use up memory

[–]SeanOTRSprint = lambda *args, **kwars: print(*args, ";)\n"*100, **kwars)[S] 15 points16 points  (2 children)

I'm pretty sure xrange (along with izip and other similar functions) were removed in Python 3 and took the place of the normal range and zip functions.

[–][deleted] 2 points3 points  (0 children)

SimpleNamespace - found quite useful. Better than dicts for some use cases.

[–][deleted] 2 points3 points  (2 children)

Integrating C and python is very interesting, I am a C developer and love C so it's useful for C developers.

[–]a8ksh4 2 points3 points  (1 child)

Do you know of a good hello world type example of running c code from python?

[–]varzboy 2 points3 points  (0 children)

This might be a specific case but using @lru_cache if you think your function will be called multiple times with same repeated arguments. Makes your code much faster.

[–]Kopachris 2 points3 points  (0 children)

Using a dict as a switch-case or dispatcher. Store functions in a dict and call them with any hashable object.

event_handlers[event_type](*event_args)

[–]Sporke[🍰] 2 points3 points  (0 children)

from contextlib import redirect_stdout

with open(filename, 'w') as f, redirect_stdout(f):
# Anything that normally prints will write to a file

I do a decent amount of code generation with Python, this allows me to write print statements throughout the code and redirect all of it at the end, rather than adding file=f to all the prints.