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
If you play around with the built-in id function, you'll see that anything you do to a string that SEEMS like it may change the string actually returns a new string instead.

>>> name = "Trey"
>>> id(name)
139687084218480
>>> name = name.lower()
>>> id(name)
139687076401968
So mutable objects can change and immutable objects can't. Simple, right?

Not so fast... tuples are where things start to get fuzzy.

At first glance, tuples seem just as immutable as strings. But unlike strings, a tuple's value can change (depending on how you define "value").
If 2 names point to the same tuple and we "change" one, we're (again) just making a new tuple:

>>> a = b = (3, 4)
>>> id(a)
139687093427712
>>> a += (5, 6)
>>> id(b) # b is the old tuple
139687093427712
>>> id(a) # a is a new one
139687084242496
>>> a
(3, 4, 5, 6)
>>> b
(3, 4)
But what if our tuple contains a mutable object?

>>> result = (True, {"name": "Trey"})
>>> again = result
>>> result[1]["name"] = "Jay"
>>> again
(True, {'name': 'Jay'})

When we changed the dictionary our tuple contains, the original tuple seemed to change too! 😦
This is possible because tuples don't "contain" objects. They just contain references (a.k.a. "pointers" or "bindings") to objects.

Our tuple still points to the same dictionary. But the dictionary it's pointing to happened to have changed.
It's a bit fuzzier though because a tuple which points to a mutable object can have its sense of equality change:

>>> result = (True, {"name": "Trey"})
>>> other = (True, {"name": "Trey"})
>>> result == other
True
>>> result[1]["name"] = "Jay"
>>> result == other
False
In general, if an immutable object can contain a reference to a mutable object (as tuples can), then it's possible to make an "immutable" object which can *seem* to "change" (by changing one of the objects it contains a reference to).
For more on mutability in #Python see:

1️⃣ This definition (and related definitions): pym.dev/terms/#mutable
2️⃣ An explanation of how Python's variables work: pym.dev/pointers/
3️⃣ @AlSweigart's
4️⃣ My #pythonoddity talk trey.io/oddities
Did you learn something new about Python? 🐍💖

For more #TerminologyTuesday tweets and daily #Python tips, follow me at @treyhunner and @PythonMorsels. 🌠

For my longer-form Python tips, get on my email newsletter: pym.dev/newsletter 📨

• • •

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 19
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 11 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!

:(