Sign in

Trey Hunner

@trey.io
4K followers 216 following 2.2K posts

Python & Django team trainer I help folks sharpen their Python skills with PythonMorsels.com 🐍🍪 I also blog at treyhunner.com & run Whereabouts.Earth PSF Fellow. YIMBY. 95% vegan.

PostsRepliesMedia
Trey Hunner @trey.io · 11h
“Fortunately, as of Python 3.12, instead of specifying delete=False, we can just specify delete_on_close=False:” Read more 👉 pym.dev/temporary-fi... #Python
pym.dev
Creating temporary files in Python
How to create temporary files and directories in Python using the tempfile module's NamedTemporaryFile and TemporaryDirectory.
020
Trey Hunner @trey.io · 16h
🗺️ September 30, 2026 5/5 countries in 6/13 guesses 🟢🟢🟢🟡🟢 whereabouts.earth/daily/ #Whereabouts
whereabouts.earth
Whereabouts Daily
How many of today's five countries can you guess?
120
Trey Hunner @trey.io · 30/09/2026
There ARE exceptions: list.pop and dict.setdefault both mutate AND return. But neither returns the object it mutated. When mutation is a function's primary purpose, returning a value isn't QUITE as confusing. If your function does BOTH, consider whether that could be a gotcha.
010
Trey Hunner @trey.io · 30/09/2026
Your own functions should follow this convention too. If a function returns data AND mutates an object, callers may incorrectly assume that no object was mutated. print(f"Median score: {median(scores)}") It would be VERY surprising if median changed our scores list.
100
Trey Hunner @trey.io · 30/09/2026
The sort method modifies the list IN PLACE: mutating the list is its purpose. Returning nothing indicates that an action happened. append, extend, update, and most other mutating methods/functions follow this convention.
100
Trey Hunner @trey.io · 30/09/2026
Python Tip #272 (of 365): Do something or return something, but not both. 🧵 The list sort method returns None: >>> numbers = [4, 2, 7, 1, 5] >>> x = numbers.sort() >>> print(x) None >>> numbers [1, 2, 4, 5, 7] #Python #DailyPythonTip
120
Trey Hunner @trey.io · 30/09/2026
Functions are objects in Python. Anywhere a function is expected (a key argument, a callback, map, defaultdict...), you can pass an EXISTING function instead of wrapping it: key=str.lower # not key=lambda s: s.lower() defaultdict(list) # not defaultdict(lambda: [])
010
Trey Hunner @trey.io · 30/09/2026
Calling lower on a string is the same thing as passing that string to str.lower: >>> name = "Python" >>> name.lower() 'python' >>> str.lower(name) 'python' Methods are just functions attached to classes (see tip #39... misnumbered in the embed below). That's the reason passing str.lower works.
bsky.app
Trey Hunner (@trey.io)
Python Tip #37 (of 365): Avoid overusing the term "method". 🧵 In Python, the words "function" and "method" are not interchangeable. All methods are functions, but not all functions are methods.…
100
Trey Hunner @trey.io · 30/09/2026
It doesn't matter whether your function was made with a "def" block or a "lambda" expression: if it's a wrapper that could be deleted, consider deleting it. def lowercase(s): return s.lower() Use str.lower directly, or better yet, ask whether the function you ACTUALLY want already exists.
100
Trey Hunner @trey.io · 30/09/2026
Python Tip #273 (of 365): Avoid needless wrapper functions. 🧵 This lambda does nothing but call len: sorted(words, key=lambda s: len(s)) So pass len itself: sorted(words, key=len) #Python #DailyPythonTip
120
Trey Hunner @trey.io · 29/09/2026
“A loop that goes on forever is called an infinite loop.” Read more 👉 pym.dev/while-loops/ #Python
pym.dev
https://pym.dev/while-loops/
010
Trey Hunner @trey.io · 29/09/2026
🗺️ September 29, 2026 5/5 countries in 7/13 guesses 🟡🟡🟢🟢🟢 whereabouts.earth/daily/ #Whereabouts
whereabouts.earth
Whereabouts Daily
How many of today's five countries can you guess?
121
Trey Hunner @trey.io · 29/09/2026
Functions that print are typically the surface-level ones, like a main function that calls other functions and displays results for a user. Other functions typically hand values BACK to the code that called them with "return". Most functions should return instead of printing.
pythonmorsels.com
The difference between return and print in Python
Both return and print "output" something, but one shows output to an end user and the other doesn't.
020
Trey Hunner @trey.io · 29/09/2026
We could find out by assigning a variable to the function's result: >>> x = nth_fibonacci(20) 6765 >>> print(x) None Output appeared DURING THE CALL, and x got None (the default return value), so this function prints instead of returning.
110
Trey Hunner @trey.io · 29/09/2026
The print function "outputs" to the TERMINAL. The return statement "outputs" to the code that called your function. The Python REPL blurs this distinction by printing every non-None return value: >>> nth_fibonacci(20) 6765 Was that printed, or returned? It's unclear.
110
Trey Hunner @trey.io · 29/09/2026
Python Tip #271 (of 365): Return, don't print. 🧵 If your function only seems to "work" in the REPL, it may be printing when it should be RETURNING. #Python #DailyPythonTip
140
Trey Hunner @trey.io · 28/09/2026
“It has nothing to do with binary numbers or ones and zeroes.” Read more 👉 pym.dev/binary-search/ #Python
pym.dev
Binary search in Python with bisect
Python's bisect module implements binary search for you. Here's how bisect_left, bisect_right, and insort work, plus recipes for finding the closest match or all values in a range.
030
Trey Hunner @trey.io · 28/09/2026
In early April this year I only knew the location of 2 of these 5 countries. 🗺️ September 28, 2026 5/5 countries in 5/13 guesses 🟢🟢🟢🟢🟢 whereabouts.earth/daily/ #Whereabouts
whereabouts.earth
Whereabouts Daily
How many of today's five countries can you guess?
130
Trey Hunner @trey.io · 28/09/2026
Before 3.14, if you wanted to copy a directory, you had to use shutil: shutil.copytree(taxes, "taxes-backup") Path.rename could only move files within one filesystem. The move method works across filesystems. If you're on 3.14+, I'd use these methods instead of the equivalent shutil functions.
pym.dev
Python 3.14's best new features
Python 3.14 includes syntax highlighting, improved error messages, enhanced support for concurrency and parallelism, t-strings and more!
000
Trey Hunner @trey.io · 28/09/2026
There are four new methods: • move(target): move to a new path • move_into(directory): move into a directory • copy(target): copy to a new path • copy_into(directory): copy into a directory The "*_into" variants save you from manually joining the directory and filename.
120
Trey Hunner @trey.io · 28/09/2026
Python Tip #270 (of 365): Move and copy files with pathlib. 🧵 In Python 3.14, Path objects gained move and copy methods, and copy works on whole directories: >>> from pathlib import Path >>> taxes = Path("taxes") >>> taxes.copy("taxes-backup") PosixPath('taxes-backup') #Python #DailyPythonTip
150
Trey Hunner @trey.io · 27/09/2026
“The reproducibility of pseudorandom numbers is useful for automated tests and for debugging.” Read more 👉 pym.dev/random-numbe... #Python
pym.dev
Selecting random values in Python
Python's random module provides utilities for generating pseudorandom numbers. For cryptographically-secure randomness, use the secrets module instead.
130
Trey Hunner @trey.io · 27/09/2026
🗺️ September 27, 2026 5/5 countries in 5/13 guesses 🟢🟢🟢🟢🟢 whereabouts.earth/daily/ #Whereabouts
whereabouts.earth
Whereabouts Daily
How many of today's five countries can you guess?
120
Trey Hunner @trey.io · 26/09/2026
For just finding files by pattern, rglob is simpler. But walk is better when you care about the directory structure, like skipping whole directories by removing them from sub_directories before walk descends into them: if ".git" in sub_directories: sub_directories.remove(".git")
docs.python.org
pathlib — Object-oriented filesystem paths
Source code: Lib/pathlib/ This module offers classes representing filesystem paths with semantics appropriate for different operating systems. Path classes are divided between pure paths, which pro...
010
Trey Hunner @trey.io · 26/09/2026
Why does walk return strings for the names instead of Path objects? Speed. When working with 100,000+ paths, the time it takes to create a new Path object is somewhat noticeable... unless you're also opening each file. Then Path creation time will probably be swallowed by the file open time.
100
Trey Hunner @trey.io · 26/09/2026
The names are strings, not Path objects. Join them to the directory: >>> for directory, sub_directories, files in taxes.walk(): ... for name in files: ... if name.endswith(".csv"): ... print(directory / name) ... taxes/2026/expenses.csv
100
Trey Hunner @trey.io · 26/09/2026
Example: >>> for directory, sub_directories, files in taxes.walk(): ... print(directory, sub_directories, files) ... taxes ['archived', '2026'] [] taxes/archived ['2017'] [] taxes/archived/2017 ['invoices'] [] taxes/archived/2017/invoices [] ['soylent.pdf'] taxes/2026 [] ['expenses.csv']
100
Trey Hunner @trey.io · 26/09/2026
Python Tip #269 (of 365): Walk directory trees with Path.walk. 🧵 The walk method (Python 3.12+) is the pathlib version of os.walk. For each directory in the tree, you get the directory's path, a list of its subdirectory names, and a list of its file names. #Python #DailyPythonTip
110
Trey Hunner @trey.io · 26/09/2026
“When exactly parentheses improve readability is a bit fuzzy and it's definitely subjective.” Read more 👉 pym.dev/unnecessary-... #Python
pym.dev
https://pym.dev/unnecessary-parentheses/
010
Trey Hunner @trey.io · 26/09/2026
🗺️ September 26, 2026 5/5 countries in 8/13 guesses 🟡🟢🟠🟢🟢 whereabouts.earth/daily/ #Whereabouts
whereabouts.earth
Whereabouts Daily
How many of today's five countries can you guess?
120
Trey Hunner @trey.io · 26/09/2026
Note that relative_to only works when the path actually IS inside the given ancestor directory. Otherwise it raises a ValueError. Passing "walk_up=True" (Python 3.12+) allows ".." in the result instead, but I rarely need that.
pym.dev
Python's pathlib module
Python's pathlib module is the tool to use for working with file paths. See pathlib quick reference tables and examples.
030
Trey Hunner @trey.io · 26/09/2026
This is handy when searching a file tree: >>> for path in taxes.rglob("*.csv"): ... print(path.relative_to(taxes)) ... 2026/expenses.csv 2026/dividends.csv The code works with full paths internally, but users only see short paths.
130
Trey Hunner @trey.io · 26/09/2026
Python Tip #268 (of 365): Show relative paths with relative_to. 🧵 Absolute paths are noisy: >>> print(path) /home/trey/taxes/archived/2017/invoices >>> taxes PosixPath('/home/trey/taxes') >>> print(path.relative_to(taxes)) archived/2017/invoices #Python #DailyPythonTip
131
Reposted by Trey Hunner
Grace @gracekind.net · 26/09/2026
“Improve model for everyone,” aka “allow my images to be posted on public websites”
“Improve model for everyone” setting in chatgpt
714610
Trey Hunner @trey.io · 25/09/2026
“A queue is often called a FIFO data structure: first in, first out.” Read more 👉 pym.dev/stacks-and-q... #Python
pym.dev
Stacks and queues in Python
Use a Python list for stack operations (last-in, first-out) and a deque from the collections module for queue operations (first-in, first-out).
040
Trey Hunner @trey.io · 25/09/2026
🗺️ September 25, 2026 5/5 countries in 5/13 guesses 🟢🟢🟢🟢🟢 whereabouts.earth/daily/ #Whereabouts
whereabouts.earth
Whereabouts Daily
How many of today's five countries can you guess?
120
Reposted by Trey Hunner
San Diego Python @sandiegopython.org · 24/09/2026
Our monthly meetup is tonight 7pm at @qualcomm.bsky.social in the usual spot. We have a great speaker lineup including @trey.io. See you there or on the livestream. www.meetup.com/pythonsd/eve...
001
Trey Hunner @trey.io · 24/09/2026
“If an object quacks like a duck, it may be used as if it's a duck.” Read more 👉 pym.dev/all-iteratio... #Python
pym.dev
https://pym.dev/all-iteration-is-the-same/
120
Trey Hunner @trey.io · 24/09/2026
Also, to change the non-extension part of a filename, you can use with_stem: >>> old_path.with_stem("README") PosixPath('uvrs/README.rst') And to replace the whole name, there's with_name: >>> old_path.with_name("Read Me.txt") PosixPath('uvrs/Read Me.txt')
pym.dev
Python's pathlib module
Python's pathlib module is the tool to use for working with file paths. See pathlib quick reference tables and examples.
020
Trey Hunner @trey.io · 24/09/2026
But even when using pathlib.Path, the alternative to with_suffix is awkward. Instead of this: new_path = old_path.parent / (old_path.stem + ".md") Do this: new_path = old_path.with_suffix(".md") Note that with_suffix doesn't actually rename the file. For that you'll want the rename method.
110
Trey Hunner @trey.io · 24/09/2026
With string methods this is a pain: >>> filename = "uvrs/readme.rst" >>> filename.rsplit(".", maxsplit=1)[0] + ".md" 'uvrs/readme.md' Also if there was no extension but there WAS a period in a directory name, that'd do the wrong thing.
110
Trey Hunner @trey.io · 24/09/2026
Python Tip #267 (of 365): Change file extensions with with_suffix. 🧵 A ".rst" file path that needs to turn into a ".md" file path: >>> from pathlib import Path >>> old_path = Path("uvrs/readme.rst") >>> old_path.with_suffix(".md") PosixPath('uvrs/readme.md') #Python #DailyPythonTip
140
Trey Hunner @trey.io · 24/09/2026
🗺️ September 24, 2026 5/5 countries in 8/13 guesses 🟢🟠🟢🟡🟢 whereabouts.earth/daily/ #Whereabouts
whereabouts.earth
Whereabouts Daily
How many of today's five countries can you guess?
120
Trey Hunner @trey.io · 24/09/2026
Keep in mind that because the loops in a comprehension must be in the same order as equivalent for loops, it does read a little funny. Read more 👉 trey.io/aeo9fg #Python
030
Trey Hunner @trey.io · 23/09/2026
Two caveats: 1. seek positions are BYTE offsets. In text mode, seeking anywhere but 0 can land you mid-character, so seek(0) is pretty much the only seek you'll use on text files. 2. Rereading a file is rare. If you need the contents twice, it's often easier to just read them into a variable once.
020
Trey Hunner @trey.io · 23/09/2026
Python keeps track of your position within a file as you read. When you're at the end, another read returns an empty string. The seek method changes that position. seek(0) moves back to the beginning. >>> f‍.seek(0) 0 >>> f‍.read() 'This is my file 👋\nThis is line 2\n'
110
Trey Hunner @trey.io · 23/09/2026
Python Tip #266 (of 365): Reread a file with seek(0). 🧵 Try to .read() a file twice, and you'll get nothing the second time: >>> f = open("my_file.txt") >>> f‍.read() 'This is my file 👋\nThis is line 2\n' >>> f‍.read() '' #Python #DailyPythonTip
130
Trey Hunner @trey.io · 23/09/2026
🗺️ September 23, 2026 5/5 countries in 5/13 guesses 🟢🟢🟢🟢🟢 whereabouts.earth/daily/ #Whereabouts
whereabouts.earth
Whereabouts Daily
How many of today's five countries can you guess?
120
Trey Hunner @trey.io · 23/09/2026
“For true randomness, you can use the secrets module, which generates cryptographically secure random numbers.” Read more 👉 pym.dev/random-numbe... #Python
pym.dev
Selecting random values in Python
Python's random module provides utilities for generating pseudorandom numbers. For cryptographically-secure randomness, use the secrets module instead.
020
Trey Hunner @trey.io · 22/09/2026
Files had a readline method years before they were iterators. I suspect that they would never have had a readline method added to them if they were iterators from the beginning. The next time you want to move a file one line forward, use the next function.
010