https://www.bitecode.dev/p/this-is-valid-python-syntax [https] Bite code! SubscribeSign in Share this post [https] This is valid Python syntax www.bitecode.dev Copy link Facebook Email Notes Other This is valid Python syntax Sometimes it's even useful too [https] Nobody has time for Python Jun 10, 2023 4 2 Share Share this post [https] This is valid Python syntax www.bitecode.dev Copy link Facebook Email Notes Other Summary * f"{0xBE_AF:.2f}" is what happens if you use "_" in hex, and interpolate it with a 2 decimal precision. * [*(_ for [[][:], *_] in [((1, 2), "woops")])] combines a generator, unpacking in a list, unpacking in a loop, and unpacking in a slice, for this delicious cryptic effect. * (lambda *Shu Zi : __import__('math').prod(Shu Zi ))(*([2]*6)) == 2**6 leverages the splat operator * for variadic parameters (as they work in a lambda) and unpacking arguments. Because prod() is not a built-in, we use the __import__ function to access the module that holds it. Shu Zi is variable name. [ ]Subscribe f"{0xBE_AF:.2f}" Python has come with a variety of notations for numbers for a long time: >>> 250 # decimal 250 >>> 0b11111010 # binary 250 >>> 0xFA # hexadecimal 250 >>> 2.5e2 # scientific notation 250.0 >>> 0xFA == 250 == 0xFA == 2.5e2 True In 2016, PEP 515 started to allow underscores in numeric literals to Python 3.6. E.G, as a thousands separator: >>> 250_000_000 == 250000000 True But the new feature is not limited to base 10: >>> 0b11_11_10_10 250 >>> 0xBEAF == 0xBE_AF == 48_815 True Now, it turns out 3.6 also added another fantastic feature, f-strings. And f-strings have the capacity to allow arbitrary code in them: >>> f"{1 + 1}" '2' They also have an entire formatting language, inherited from format() , with pretty sweet capabilities: >>> f"{1:03}" # pad with zero '001' >>> f"{3.14957:.3f}" # choosing decimal precision '3.150' And because those features can be combined, if you use "_" in hex, and interpolate it with a 2 decimal precision, you get: >>> f"{0xBE_AF:.2f}" '48815.00' Of course, while all those nice toys are useful independently, I would not advise you to mix all of them too often. That doesn't scream readability, does it? [*(_ for [[][:], *_] in [((1, 2), "woops")])] While not as powerful as destructuring in other languages (I still wish we could unpack dicts as JS does with objects), unpacking is still much more interesting than most people thinks. When people say "unpacking", they usually mean one of those in their head... Putting iterable content in variables: >>> a, b, c = range(3) >>> a 0 >>> b 1 >>> c 2 Inverting variables: >>> a, b = b, a >>> a 1 >>> b 0 (don't know why this is so famous, I never, ever used that in prod) And because unpacking works in for loops, iterating on a dictionary: >>> score = {"red": 1, "blue": 2} >>> for team, points in score.items(): ... print(team, ":", points) ... red : 1 blue : 2 But unpacking packs more than that. It can assign nested structures: >>> [(team1, score1), (team2, score2)] = score.items() >>> team1 'red' >>> score1 1 If you don't care about other values, you can use * to capture them: >>> first, *others = ["Matrix 1", "Matrix 2", "Matrix 3", "Matrix 4"] >>> first 'Matrix 1' >>> others ['Matrix 2', 'Matrix 3', 'Matrix 4'] Also * has a completely different meaning, it can populate a list by unpacking other iterables in it: >>> [*"abc", *range(3)] ['a', 'b', 'c', 0, 1, 2] This also works for tuples and sets, and ** will populate mapping, but let's not make this article too big. Now, for a new trick, Python allows you to assign to slices: >>> numbers = list(range(10)) >>> numbers[3:6] = ["III", "IV", "V"] >>> numbers [0, 1, 2, 'III', 'IV', 'V', 6, 7, 8, 9] It seems weird, but it's extremely performant in Python, where loops are super slow. Well, it gets weirder when you realize you can actually use unpacking AND assign to slices together. Imagine you have a list of RGB pixels: img = ["r", "g", "b"] * 10_000_000 And you want to turn it into a BGR pixels. You can use a loop: >>> %%timeit ... img = ["r", "g", "b"] * 10_000_000 ... iterator= iter(img) ... groups= zip(iterator, iterator, iterator) ... img = [pixel for group in groups for pixel in group[::-1]] ... ... 1.15 s +- 18.2 ms per loop (mean +- std. dev. of 7 runs, 1 loop each) But using slicing and unpacking is about 5 times faster: >>> %%timeit ... img = ["r", "g", "b"] * 10_000_000 ... img[:-1:3], img[2::3] = img[2::3], img[:-1:3] ... ... 279 ms +- 2.99 ms per loop (mean +- std. dev. of 7 runs, 1 loop each) To understand the title on this section, you also need to realize _ is a valid variable name in Python. In fact, we use it to say we want to ignore a value. Now we can decrypt the hieroglyphs. First, we assigned to a slice. But look at that, we can omit the variable to hold the list, and it still works. It's useless, but it works: >>> a = [] >>> a[:] = (1, 2) >>> a [1, 2] >>> [][:] = (1, 2) So this beast: >>> [][:], *_ = ((1, 2), "woops") >>> _ ['woops'] Is unpacking (1, 2) to a slice on the left, and "woops" to *_ on the right. We can put it in a for loop because unpacking is legal in there: >>> for [[][:], *_] in [((1, 2), "woops")]: ... print(_) ... ['woops'] And if it can go in a for loop, it can go into a comprehension list: >>> [_ for [[][:], *_] in [((1, 2), "woops")]] [['woops']] A comprehension list can be turned into a generator, we just have to swap brackets for parentheses. And we can unpack it in a list: >>> gen = (_ for [[][:], *_] in [((1, 2), "woops")]) >>> [*gen] [['woops']] Finally: >>> [*(_ for [[][:], *_] in [((1, 2), "woops")])] [['woops']] Do you want this monstrosity to be written anywhere? Certainly not, but by decoding it, we learned a ton about Python. (lambda *Shu Zi : __import__('math').prod(Shu Zi ))(*([2]*6)) == 2**6 Last one for the road? There are no way to manipulate pointers from Python syntax, but splat operator (*) is still used for a lot of different cases. First, putting a number to a power of something in Python is not ^, but **: >>> 3**4 81 Then, you have variadic parameters, which allows you to declare an infinite number of params for a function: >>> def a_lot(*of_stuff): # accept 0, 1 or more params ... print(of_stuff) ... >>> a_lot(1, 2, 3) # they are stored in a tuple in of_stuff (1, 2, 3) No need to call it *args by the way. This is just the naming convention used for "I have no idea what's going in there", like for decorators. Turns out, this works with lambdas: >>> lambda *of_stuff: print(of_stuff) at 0x7f5093e39510> One tricky thing that beginners get confused about, is that you can also use * to unpack arguments in a function call: >>> def square_area(x1, y1, x2, y2): ... side_length = abs(x2 - x1) ... area = side_length ** 2 ... return area ... coords = [0, 0, 3, 3] >>> square_area(coords[0], coords[1], coords[2], coords[3]) 9 >>> square_area(*coords) 9 It's easy to get mixed up between def func(*values) (declare func with infinite params) and func(*values) (call func while unpacking arguments). Let's add one last use case for *, initializing a list: >>> [0] * 10 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] And we are almost there. Now for the final touch, we must understand import is a statement in Python, meaning it must exist on its own line. But there is a function __import__ that can also import modules, and calling it is an expression, so we can use it inside another expression: >>> import sys >>> print("dev mode:", sys.flags.dev_mode) # if you don't know dev_mode, check it out! dev mode: False >>> print("dev mode:", import sys) Cell In[126], line 1 print("dev mode:", import sys) ^ SyntaxError: invalid syntax >>> print("dev mode:", __import__('sys').flags.dev_mode) dev mode: False Now all together! We create a list of 2: >>> [2]*6 [2, 2, 2, 2, 2, 2] This can be passed to the math.prod function: >>> __import__('math').prod([2, 2, 2, 2, 2, 2]) 64 Giving us the exact same value as: >>> 2**6 64 Let's make it needlessly complicated with a function that accepts infinite parameters, then unpack the list in it: >>> def useless_prod_wrapper(*numbers): ... return __import__('math').prod(numbers) ... >>> useless_prod_wrapper(*[2, 2, 2, 2, 2, 2]) 64 Hey, they cancel each other :) This can be a lambda: >>> useless_prod_wrapper = lambda *numbers: __import__('math').prod(numbers) But why give a name to a lambda? We can call it directly: >>> ( lambda *numbers: __import__('math').prod(numbers) )(*[2, 2, 2, 2, 2, 2]) 64 Well, if you put them all together, and translate "numbers" into simplified chinese, you get our magic formulas: >>> (lambda *Shu Zi : __import__('math').prod(Shu Zi ))(*([2]*6)) == 2**6 True Yep, some unicode characters are valid Python variable names. Once again, don't do this to your colleagues. Or to yourself. Yet, while we can't fight against masters like C++ or Perl, Python is perfectly capable of being unreadable if you beat it with a hammer. What else? Python has this wonderful quality of allowing people to be rapidly productive with the core language and stdlib. But you can dig deeper and deeper into the languages for years and still learn things. For example, did you know that the else keyword can be used with for , while and try? Have fun with the learning curve, it's a long one, but it's smooth and rewarding. print('!ebircsbus'[::-1]) [ ]Subscribe 4 2 Share Share this post [https] This is valid Python syntax www.bitecode.dev Copy link Facebook Email Notes Other 2 Comments [https] [ ] Chitranshu Vashishth 49 min agoLiked by Nobody has time for Python This was an amazing article that forced me to dive deep into the rabbit hole of Python documentation and read all the documentation. I think avoiding for loops explicitly can be an amazing mantra to discover a lot of "pythonic" ways to do things that someone is used to doing in other languages. Just wanted to suggest a correction in the article. [https] > img[:-1:3], img[1::3] = img[1::3], img[:-1:3] This creates sequences of ['g', 'r', 'b', 'g', 'r', 'b', ...] I think the correct code would be img[:-1:3], img[2::3] = img[2::3], img[:-1:3] ['b', 'g', 'r', 'b', 'g', 'r', ....] Expand full comment Reply 1 reply by Nobody has time for Python 1 more comment... Top New Community No posts Ready for more? [ ]Subscribe (c) 2023 Bite Code! Privacy [?] Terms [?] Collection notice Start WritingGet the app Substack is the home for great writing This site requires JavaScript to run correctly. Please turn on JavaScript or unblock scripts