Trey Hunner (Python trainer) Profile picture
May 19 โ€ข 12 tweets โ€ข 4 min read
I love Python's pathlib module. ๐Ÿ’–๐Ÿ๐Ÿ—ƒ

Whenever I need to ask a question about a file path in #Python, I default to using pathlib.

Let's talk about some of my most common uses for pathlib and compare them to their non-pathlib alternatives. ๐Ÿงต
Assume all the below examples include the "filename" and "path" variables defined like this:

>>> from pathlib import Path
>>> filename = "my_file.txt"
>>> path = Path(filename)

Also assume that we've imported os, glob, and os.path as needed:

>>> import glob, os, os.path
Get an absolute path (from a path that may be relative or may already be absolute).

๐Ÿ’พ Old approach:

>>> filename = os.path.abspath(filename)

โœจ pathlib approach:

>>> path = path.resolve()
Read entire file contents into a string.

๐Ÿ’พ Old approach:

>>> with open(filename) as f:
... contents = f.read()
...

โœจ pathlib approach:

>>> contents = path.read_text()
Get just the filename from a path

๐Ÿ’พ Old approach:

>>> name = os.path.basename(filename)

โœจ pathlib approach:

>>> name = path.name
Get a sibling path in the same directory

๐Ÿ’พ Old approach:

>>> filename2 = os.path.join(os.path.dirname(filename), "config.toml")

โœจ pathlib approach:

>>> path2 = path.parent / "config.toml"
Change the file extension

๐Ÿ’พ Old approach:

>>> os.rename(filename, os.path.splitext(filename)[0] + ".csv")

โœจ pathlib approach:

>>> path.rename(path.with_suffix(".csv"))
Check whether given path is a file

๐Ÿ’พ Old approach:

>>> if not os.path.isfile(filename):
... print(f"Not a file: {filename}")
...

โœจ pathlib approach:

>>> if not path.is_file():
... print(f"Not a file: {filename}")
...
Find all .py files in and below this file's directory:

๐Ÿ’พ Old approach:

>>> parent = os.path.dirname(filename)
>>> py_paths = glob.iglob("**/*.py", root_dir=parent, recursive=True)

โœจ pathlib approach:

>>> py_paths = path.parent.rglob("*.py")
And keep in mind that Path objects are accept nearly anywhere a string-representing-a-path is accepted.

>>> with open(path) as f:
... for line in f:
... print(line.rstrip())
...

Even utilities like subprocess.run and shutil.move accept Path-like objects.
๐Ÿ“บ This pathlib infomercial was brought to you by

โ€ข A need to work with file paths in Python
โ€ข, My dislike of awkwardly nested function calls
โ€ข, An appreciation for using concrete objects instead of just passing strings around everywhere

Follow me for more #Python tips. ๐Ÿ๐ŸŒ 
I keep seeing replies about how Path objects need to be converted to strings to actually use them. This was true (& was the reason I didn't like pathlib in Python 3.4), but it's not true anymore.

That's no longer a reason not to use pathlib.

โ€ข โ€ข โ€ข

Missing some Tweet in this thread? You can try to force a refresh
ใ€€

Keep Current with Trey Hunner (Python trainer)

Trey Hunner (Python trainer) Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

PDF

Twitter may remove this content at anytime! Save it as PDF for later use!

Try unrolling a thread yourself!

how to unroll video
  1. Follow @ThreadReaderApp to mention us!

  2. From a Twitter thread mention us with a keyword "unroll"
@threadreaderapp unroll

Practice here first or read more on our help page!

More from @treyhunner

May 17
Here's a deceptively fuzzy #Python term: mutable object. ๐Ÿค”

mutable object: an object whose "value" can change

But what does "value" really mean?

#TerminologyTuesday
Lists are mutable because a list object can be changed.

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

If two variables refer to the same list, any change to the list will be reflected by both variables:

>>> x = numbers
>>> x.clear()
>>> numbers
[]
Strings are immutable in #Python. That means that there is no way to "change" a string object in Python.

Try as hard as you might, you cannot change strings. You can only make NEW strings.

name = "Trey"
name = name + "!" # makes a new string
name += "!" # same as above
Read 12 tweets
May 9
Need to flatten a list-of-lists (or an iterable-of-iterables) in #Python?

There are about 5 "right" ways to do it and one "wrong" way (for some values of right and wrong). Which way you choose depends on your preference.
You may reach for nested loops:

names = []
for group in groups:
for name in group:
names.append(name)

But the extend method can replace the inner loop:

names = []
for group in groups:
names.extend(group)

If you prefer, += works as well:

names += group
I prefer to replace that for-for-append pattern with a comprehension:

names = [
name
for group in groups
for name in group
]

I find 2-loop comprehensions readable as long as each "for" component is on its own line of code. Whitespace is your friend! ๐Ÿ’“
Read 7 tweets
Feb 14
Strings in #Python have a ton of methods on them.

I recommend learning these 10 first:

โ€ข join
โ€ข split
โ€ข replace
โ€ข format
โ€ข startswith
โ€ข endswith
โ€ข strip
โ€ข casefold
โ€ข splitlines
โ€ข count

Let's talk about what these methods do and why I recommend these ones first. ๐Ÿงต
1๏ธโƒฃ The string join method accepts an iterable of strings to join together.

It should be called on the separator you'd like to join by (e.g. " ", "\n", ",", ""). This is a string method: it's " ".join(my_list) and not my_list.join(" ").

2๏ธโƒฃ The string split method is the opposite of join.

split accepts an optional separator:

time = "02:27"
h, m = time.split(":")

The default separator is "any consecutive whitespace characters".

Splitting can also be limited with a maxsplit arg.

Read 19 tweets
Dec 30, 2021
While teaching comprehensions I sometimes see students do this:

numbers = [2, 1, 3, 4, 7, 11]
squares = []
[squares.append(n**2) for n in numbers]

Comprehensions are for building up a new list, but that one above actually builds up *two* new lists. ๐Ÿงต
From a Python prompt, you can see what's going on:

>>> [squares.append(n**2) for n in numbers]
[None, None, None, None, None, None]

Comprehensions return a new list and the items in that list come from that first expression in the comprehension. squares.append returns None. ๐Ÿ˜ฆ
We *do* modify the squares list:

>>> squares
[4, 1, 9, 16, 49, 121]

But that's a side effect, which comprehensions aren't supposed to have. Here's another comprehension with a side effect:
>>> [print(n**2) for n in numbers]
4
1
9
16
49
121
[None, None, None, None, None, None]
Read 7 tweets
Oct 27, 2021
In Python, variables and data structures don't "contain" objects.
This idea is easy to miss when new to Python and it can be hard to internalize.

Let's talk about this. ๐Ÿงต

Note: "value" and "object" are synonyms in this thread and so are "variable" and "name".
Variables in Python aren't "buckets containing things" but "pointers" (they point to values). The word pointer may sound scary (it is scary in C), but it mostly means what it sounds like.

An arrow connects each variable to a value and 2 variables can point to the same value. โžก
Assignment statements point a variable to a value. That's it. Python doesn't copy anything when you assign.

So this is an odd thing to do:
>>> x = y

Because we've pointed two names to the same value. So what? Why does that matter? ๐Ÿค”
Read 16 tweets

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just two indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3/month or $30/year) and get exclusive features!

Become Premium

Don't want to be a Premium member but still want to support us?

Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal

Or Donate anonymously using crypto!

Ethereum

0xfe58350B80634f60Fa6Dc149a72b4DFbc17D341E copy

Bitcoin

3ATGMxNzCUFzxpMCHL5sWSt4DVtS8UqXpi copy

Thank you for your support!

Follow Us on Twitter!

:(