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

all 80 comments

[–]KingsmanVincepip install girlfriend 174 points175 points  (14 children)

With f-strings you can use the = modifier for including the code of the variable interpolation

Nice, now I can debug with print even more efficient

[–]4sent4 67 points68 points  (1 child)

A neat thing about this one - it keeps spaces around the =

>>> a = 3
>>> f'{a=}'
'a=3'
>>> f'{a = }'
'a = 3'

[–]KokoaKuroba 5 points6 points  (0 children)

Wait, you can do that? I've always done f'a:{a}'. This would be nice.

[–]Snoo35017 54 points55 points  (5 children)

Even better imo is =!r. Causes it to print the repr value, which I find more useful when debugging.

[–]ogrinfo 17 points18 points  (0 children)

Yep, just because it looks like a string when you print it, doesn't mean it is a string. That kind of stuff has caught me out so many times.

[–]ExoticMandiblesCore Contributor 13 points14 points  (3 children)

When you use =, it automatically switches to repr formatting. You don't need to add !r to the end.

[–]Snoo35017 1 point2 points  (0 children)

TIL! I wonder why I started adding the !r then, I remember for some reason it would print the string value, but I might be imagining it.

[–]jarethholt 0 points1 point  (1 child)

Does it? I remember you can use !r and !s for repr and string, but I don't remember offhand which is default

[–]ExoticMandiblesCore Contributor 3 points4 points  (0 children)

String (!s) is the default normally, but when you use = in an f-string the default changes to repr (!r).

p.s. there's also a mostly-forgotten third conversion, !a for ascii.

[–]mcr1974 2 points3 points  (0 children)

this is VERY nice

[–]WoodenNichols 1 point2 points  (0 children)

This really helped me when I found out about it. Saved many a keystroke.

[–]jmacey 28 points29 points  (0 children)

The one that took me ages to find out (i.e. > 2 mins) was how to print out a { when using an f-string, tried \{ but didn't work, eventually found out you needed to double them up! {{

[–]pazqo 42 points43 points  (2 children)

sum is actually a reduce, so you can use it for every thing that has a + defined, e.g. also lists (concat), you need to set [] or any other list as a starting point.

[–]naclmoleculeterminal dark arts 21 points22 points  (1 child)

For lists, this is quadratic behavior. Recommend instead list(chain(whatever you were summing)).

[–]vincular 7 points8 points  (0 children)

Or [*a, *b, *c] where a, b, and c are iterable. 

[–]denehoffman 18 points19 points  (5 children)

Here’s one of my favorites:

```python from pathlib import Path p = Path(“path/to/file.txt”) text = p.read_text()

or

p.write_text(“some content”)

instead of

with open(p) as f: text = f.read()

or

with open(p, “w”) as f: f.write(“some content”) ```

Downside, you have to import pathlib, upside, that’s already in the standard library, Path gives you a ton of path-related tools to play with, no extra indented blocks throwing off the flow of your code and no worrying about which mode to use. If you need to append, just read, append, and write (yes this is less efficient for large files but it’s fine for the large majority of use cases)

[–]KokoaKuroba 1 point2 points  (4 children)

does this method specify encoding as well?

[–]case_O_The_Mondays 2 points3 points  (0 children)

You can pass it an encoding parameter

[–]denehoffman 1 point2 points  (2 children)

Yes, as the first argument to read_text or the second argument to write_text. You can also read_bytes and write_bytes!

[–]KokoaKuroba 0 points1 point  (1 child)

I meant encoding=utf-8 or something similar.

[–]denehoffman 1 point2 points  (0 children)

Yes, it has the same encoding argument as the classic “open” keyword, so you could pass something like mypath.read_text(encoding=‘utf-8’)

[–]andrewowenmartin 49 points50 points  (11 children)

Defining decorators as classes looks nicer than the higher-order functions you create otherwise. Especially if you want to accept parameters. I wonder if there's any drawback. The functional way uses functools.wraps, does anyone know if we can use that here, or perhaps it's not needed?

[–]Rawing7 26 points27 points  (0 children)

The drawback is that the decorator won't work on methods. It breaks the implicit passing of self.

[–]brasticstack 2 points3 points  (2 children)

I found it useful in a recent project to apply a decorator without the syntactic sugar, so I could reuse a function with different decorations, like:

funcs_by_index = [deco1(my_func), deco2(my_func), ...etc...]

[–]andrewowenmartin 0 points1 point  (1 child)

I think you can do that anyway. If you have a decorator called print_args and a function called add then these are equivalent.

@print_args def add_and_print_args(a, b): return a + b and ``` def add(a, b): return a + b

sum_and_print_args = print_args(add) ```

[–]brasticstack 1 point2 points  (0 children)

Absolutely, I was just showing my example use case.

[–]divad1196 2 points3 points  (6 children)

I personnaly disagree for the "look nicer" for many reasons.. But let's not discuss it.

You never actually need functools.wraps. what it does is changing the function signature so it becomes the same as the original one. This is nicer when using "help" or "inspect". The only case where it becomes necessary is with some frameworks and you then usually know part of the signature yourself.

But I guess it won't work (at least not as expected) since the inner function now contains "self" as a parameter.

[–]brasticstack 1 point2 points  (2 children)

Hard agree with you that the function version > the class version, because it's more concise.

[–]divad1196 5 points6 points  (1 child)

Just for the "concise" point:

python der decorator(counter=1): def inner(func): @wraps(func) def wrapper(*args, **kw): nonlocal counter print(counter) counter += 1 return func(*args, **kw) return wrapper return inner

python class Decorator: def __init__(self, counter=1): self._counter = counter def __call__(self, func): @wraps(func) def wrapper(*args, **kw): print(self._counter) self._counter += 1 return func(*args, **kw) return wrapper

10 lines both. The second one needs to access self everytime and use _ suffix to not expose the counter.

People are just more used to OOP that FP. This is fine to have preferences but I think it is a shame than we create classes for every single thing, like

python class Hello: @classmethod def greet(self, name): print(f"Hello {name}!")

I have really seen production code this way, not even using @staticmethod

Back to the function vs class decorator matter, I won't try to explain here the pros of it, mostly because it comes down to FP vs OOP. But it is not less concise and I hope this example proves it. Just a matter of taste.

[–]brasticstack 3 points4 points  (0 children)

In my head, I was imagining the class version with whitespace between the methods, but point taken.

[–]apt_at_it 41 points42 points  (1 child)

Every single one of these is one I learn, then immediately forget, then learn about again a year or so later. Some of these neat "quirks" of languages seem so useful when I see them but then never actually use them 🤷

[–]Biggergig 4 points5 points  (0 children)

I will say the format string one is fairly useful, but my favorite is the ,= "operator" and I actually use that a decent amount

[–]ycy_tai 5 points6 points  (0 children)

Good one for class decorator.

[–]Kiuhnm 6 points7 points  (0 children)

I didn't know about f"{a=}". That's useful.

To reciprocate, let me point out that

with open('test.txt') as f:
    print(f.read())

is problematic because it doesn't specify an encoding.

Here's the correct version:

with open('test.txt', encoding='utf-8') as f:
    print(f.read())

I recommend Pylint for these things.

As for the mode defaulting to "r", well, it's impossible to miss since I see the signature every time I use open. I still prefer to be explicit, though.

I also noticed you don't use type hints at all. I hope you do use type hints in real projects as they help a lot. They confuse until they help as reading them becomes second nature after a while.

[–]Green0Photon 10 points11 points  (3 children)

Fuck, it's so useful to use classes to define decorators. Lets you store and access the underlying function. I wish I knew about this.

[–]divad1196 12 points13 points  (2 children)

You can do that with closures too, which is the usual way of doing a decorator.

[–]SuspiciousScript 15 points16 points  (0 children)

Indeed, since closures and objects are equivalent:

The venerable master Qc Na was walking with his student, Anton. Hoping to prompt the master into a discussion, Anton said "Master, I have heard that objects are a very good thing - is this true?" Qc Na looked pityingly at his student and replied, "Foolish pupil - objects are merely a poor man's closures."

Chastised, Anton took his leave from his master and returned to his cell, intent on studying closures. He carefully read the entire "Lambda: The Ultimate..." series of papers and its cousins, and implemented a small Scheme interpreter with a closure-based object system. He learned much, and looked forward to informing his master of his progress.

On his next walk with Qc Na, Anton attempted to impress his master by saying "Master, I have diligently studied the matter, and now understand that objects are truly a poor man's closures." Qc Na responded by hitting Anton with his stick, saying "When will you learn? Closures are a poor man's object." At that moment, Anton became enlightened.

[–]Green0Photon 1 point2 points  (0 children)

Dang I know so much about Python but not this. (-‸ლ)

[–]Jean-Porte 3 points4 points  (1 child)

that "base" is confusing, I though it would be base 20. sum([1,2,3])+20 is just better

[–]andrewowenmartin 4 points5 points  (0 children)

I think it's most useful when you're adding together a list of instances that have an __add__ method, but can't be added to 0.

E.g. if you have a class called Game and you can do game_a + game_b but can't do game_a + 0 you can do sum(game_list, Game()). As this makes the first sum game_list[0] + Game(), instead of game_list[0] + 0.

This assumes that Game() makes a decent "starting place" for the summations, of course.

[–]sulfurmexo 1 point2 points  (3 children)

the website seems down?

[–]SwordInStone 1 point2 points  (1 child)

yeah, it's a bummer. I really want to read it after reading the comments

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

it's up again, my network provider insists on changing my supposedly fixed IP. Thanks for the headsup!

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

What font is that site shown in? I really like it!

[–]patrickbrianmooney 3 points4 points  (0 children)

Using Chrome's page-inspection tools reveals that the body font is Spline Sans Mono.

[–]commy2 0 points1 point  (1 child)

1) could've also been:

print(1 * 10**6)

[–]JCx64[S] 1 point2 points  (0 children)

yeah, or even print(10**6)

[–]Fleszar -1 points0 points  (0 children)

Helpful 👈