1
0
mirror of https://github.com/jbranchaud/til synced 2026-07-05 08:58:23 +00:00

Compare commits

...

3 Commits

Author SHA1 Message Date
jbranchaud 0cb5890fc0 Add Access Variables Outside Loop Scope as a Python TIL 2026-04-21 17:48:06 -05:00
jbranchaud 7de0e70d78 Add Define A Set Of Class Methods as a Ruby TIL 2026-04-17 10:51:31 -04:00
jbranchaud 36934aa56f Add Make Dataclass Sortable By Specific Field as a Python TIL 2026-04-15 22:53:29 -05:00
4 changed files with 139 additions and 1 deletions
+4 -1
View File
@@ -10,7 +10,7 @@ working across different projects via [VisualMode](https://www.visualmode.dev/).
For a steady stream of TILs, [sign up for my newsletter](https://visualmode.kit.com/newsletter).
_1775 TILs and counting..._
_1778 TILs and counting..._
See some of the other learning resources I work on:
@@ -1048,6 +1048,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Access Instance Variables](python/access-instance-variables.md)
- [Access Most Recent Return Value In REPL](python/access-most-recent-return-value-in-repl.md)
- [Access Variables Outside Loop Scope](python/access-variables-outside-loop-scope.md)
- [Avoid Modification With Frozen Dataclass](python/avoid-modification-with-frozen-dataclass.md)
- [Break Debugger On First Line Of Program](python/break-debugger-on-first-line-of-program.md)
- [Check If Package Is Installed With Pip](python/check-if-package-is-installed-with-pip.md)
@@ -1063,6 +1064,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Keep A Tally With collections.Counter](python/keep-a-tally-with-collections-counter.md)
- [Load A File Into The Python REPL](python/load-a-file-into-the-python-repl.md)
- [Look Inside Pytest tmp_path](python/look-inside-pytest-tmp-path.md)
- [Make Dataclass Sortable By Specific Field](python/make-dataclass-sortable-by-specific-field.md)
- [Override The Boolean Context Of A Class](python/override-the-boolean-context-of-a-class.md)
- [Parse Relative Time To datetime Object](python/parse-relative-time-to-datetime-object.md)
- [Skip Specific Pytest Test Cases](python/skip-specific-pytest-test-cases.md)
@@ -1419,6 +1421,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Defaulting To Frozen String Literals](ruby/defaulting-to-frozen-string-literals.md)
- [Define A Custom RSpec Matcher](ruby/define-a-custom-rspec-matcher.md)
- [Define A Method On A Struct](ruby/define-a-method-on-a-struct.md)
- [Define A Set Of Class Methods](ruby/define-a-set-of-class-methods.md)
- [Define Multiline Strings With Heredocs](ruby/define-multiline-strings-with-heredocs.md)
- [Destructure The First Item From An Array](ruby/destructure-the-first-item-from-an-array.md)
- [Destructuring Arrays In Blocks](ruby/destructuring-arrays-in-blocks.md)
@@ -0,0 +1,44 @@
# Access Variables Outside Loop Scope
Here is a function that loops over a list to find the first occurrence of a
falsy value.
```python
def find_false(self):
for item in self.items:
item_type = type(item)
print(f"Current item: {item} ({item_type})")
if not item:
break
print(f"First false item: {item} ({item_type})")
```
Notice how at the end of the function, outside of the loop, I am able to access
both `item` (defined in the loop definition) and `item_type` (defined within the
loop's body).
Both of these variables are defined, by the loop, in _function scope_ and are
accessible anywhere in the function after they have been defined.
The title of this TIL is a bit of a misnomer because Python doesn't have the
concept of a _loop scope_. There are two levels of scope in Python --
module/global scope and function scope.
I spend most of my time writing Ruby which also has _block scope_, so Python's
simplified two-level scoping took me by surprise.
Though the code sample above is contrived, this function scope assignment can be
taken advantage of with loop definitions in scenarios where you want to know
what the last `item` defined was before the loop terminated.
```python
for submission in submissions:
if passes(submission, criteria):
break
else:
raise ValueError("No submissions that meet given criteria")
print(f"Submit first passing submission: {submission.id}")
submit(submission)
```
@@ -0,0 +1,45 @@
# Make Dataclass Sortable By Specific Field
One way to sort a list of some `dataclass` is to define the `key` parameter when
calling `sort` or `sorted` like I discussed in [Sort a List of Dataclass
Instances](sort-a-list-of-dataclass-instances.md):
```python
for date in sessions_grouped_by_day.keys():
sessions_grouped_by_day[date].sort(
key=lambda session: session.start_time.time()
)
```
But then that lambda for `key` needs to be defined everywhere you sort.
If the dataclass has a single, specific field that acts as a natural proxy for
sort order, then you can define that in the `dataclass` implementation with the
`__lt__` method.
As long as a class defines the _less than_ dunder method, it will be sortable.
Here is what that looks like for this `Session` dataclass:
```python
from dataclasses import dataclass
from datetime import datetime, timezone
@dataclass
class Session:
start_time: datetime
project_name: str
end_time: datetime | None = None
def __lt__(self, other):
if not isinstance(other, Session):
return NotImplemented
return self.start_time < other.start_time
# more methods below ...
```
This implementation of `__lt__` tells the sorting methods that _this_ (`self`)
instance of `Session` can be compared to some `other` instance of `Session` by
comparing their `start_time` values to see which is less than. The guard at the
beginning makes sure only instances of `Session` are being compared.
+46
View File
@@ -0,0 +1,46 @@
# Define A Set Of Class Methods
The most common way to define class methods is by defining them directly with
`self` (the class in the current context) on a method by method basis:
```ruby
class User
def self.find_by(attrs)
# lookup logic ...
end
end
```
If you have a group of class methods you want to define, you can stick them all
within a `class << self` block which does similarly defines each of them as
singleton methods of that class (`User` in this case):
```ruby
class User
class << self
def find_by_email(email)
# lookup logic ...
end
def find_by_last_name(last_name)
# lookup logic ...
end
end
end
```
This opens the singleton class of `User` for modification, adding these two new
methods.
We can see those defined alongside all other direct and inherited class methods:
```ruby
> User.methods
=>
[:find_by_email,
:find_by_last_name,
:yaml_tag,
:allocate,
...
]
```