That's not the only surprise when it comes to assignment in Python though.
When we talk about assigning variables and assignments, we're often referring to assignment statements (a line with an = sign on it).
But that's not the only way to assign a variable in Python! 😕
For loops also assign to variables in each iteration of their loop.
Watch "x" get reassigned here:
>>> x = 0
>>> for x in range(3):
... print(x)
...
0
1
2
>>> x
2
The last iteration of the loop assigned it to 2!
There are assignments hiding all over...
Function definitions assign a variable (the function name) to a new function object:
>>> x
2
>>> def x(): pass
...
>>> x
<function x at 0x7fbb8a10a560>
Class definitions assign variables to a new class object:
>>> class x: pass
...
>>> x
<class '__main__.x'>
Even import statements perform assignments.
>>> import json
>>> json
<module 'json' from '/usr/lib/python3.11/json/__init__.py'>
The "from" syntax performs many assignments:
>>> from math import e, tau
>>> e
2.718281828459045
>>> tau
6.283185307179586
Another surprise: assignment statements can sometimes mutate! 🤔
Specifically, augmented assignment statements (a.k.a. in-place assignments) such as += and *= will typically mutate the object being assigned if it's mutable.
>>> x = y = []
>>> x += [1, 2]
>>> y
[1, 2]
One more big fact about assignment in Python:
Assignment changes LOCAL variables only*
>>> x = 0
>>> def f():
... x = 4
...
>>> f()
>>> x
0
If there's a global variable of the same name, it will be "shadowed" but left unchanged.
Pedants may say "but wait, you CAN assign to global and nonlocal variables in Python by using special statements".
That's right, but you usually shouldn't. It's rarely a good idea and often an indication that your code could benefit from well-placed return statements or a class.
I'll leave a bigger discussion of namespaces and scope for another day... but I'd like to close this thread by linking to over a dozen screencasts on @PythonMorsels on assignment, mutation, and scope.
And methods like isnumeric, isdigit, and isdecimal unfortunately take a bit of studying to figure out, so I recommend either avoiding them or using them carefully.
Let's discuss:
• The main reason generators are used
• The 2 ways to make a generator
• Lesser known generator features
• Why and when to use generators
Thread 🧵👇
Generators are typically used as lazy iterables.
By "lazy" I mean that they don't actually compute their values until they absolutely need to.
Essentially generators will "generate" their next value as soon as they're asked for it.
Here's a generator of every .py file in my home directory:
>>> from pathlib import Path
>>> py_files = Path.home().rglob("*.py")
>>> py_files
<generator object Path.rglob at 0x7f5a6721c900>
(the rglob method on pathlib.Path objects always returns a generator)
Usually when we think of an "object" we think of class instance. For example these are all objects:
>>> numbers = [2, 1, 3, 4, 7] # a list object
>>> colors = {"red", "green", "blue", "yellow"} # a set object
>>> name = "Trey" # a string object
>>> n = 3 # an int object
Need to remove all spaces from a string in #Python? 🌌🐍
Let's take a quick look at:
• removing just space characters
• removing all whitespace
• collapsing consecutive whitespace to 1 space
• removing from the beginning/end
• removing from the ends of every line
Thread🧵
If you just need to remove space characters you could use the string replace method to replace all spaces by an empty string: