-
Notifications
You must be signed in to change notification settings - Fork 0
1. Whitespaces and Newlines
This chapter is using the same representations of spaces and tabs in IFcoltransG's esoteric python guide (·
for space and ↹
for tab).
In PEP8, 4 spaces is the recommended indentation; no tabs are used as indentation; and spaces are put around operators (except for .
and the unary operators). But anyways, to make esoteric code, we can choose not to do that.
Figure 1.1
if ↹ 1:
↹ ↹ · ↹ ↹ · 'Hey, this works!'
· ↹ · ↹ ↹ · "But this doesn't work, because we replaced the first tab with a space"
↹ ↹ ↹ · ↹ · "This also doesn't work, because the order of spaces and tabs are switched"
Figure 1.2
while · 2:
· · · 'We can use three spaces'
· · · [
· · · · · ↹ · · · 'But in these hanging indents',
· 'We can use any indentation we want!',
· · · ↹ ↹ 'This works',
↹ · ↹ · ↹ · · 'This also does',
↹ · ↹ · ↹]
· · · if'a': # We can use absolutely no spaces at all between a keyword and a string
↹ · ↹ · ↹ · "This doesn't work, because the first three characters for indentation is a tab, a space, and a tab instead of three spaces"
· · · ↹ · ↹ · ↹ · 'But this works, because the first three characters for indentation are three spaces'
Figure 1.3
2. # There's a difference between this
2 · . # And this (this causes a `SyntaxError`); the explanation is if there's some one-line whitespace[s] between an object and a dot, the dot is
interpreted as an attribute access operator instead
print(2 ↹ · ↹ . ↹ ↹ ↹ ↹ ↹ ↹ ↹ ↹ ↹ ↹ __class__)
<class 'int'>
print(2..__class__)
<class 'float'>
·
for space and ↹
for tab ends in this page from here
Confusing things can be made when spaces are put where they shouldn't be put. Like this example:
a = [0x_for x in (1, 2, 3)]
print(a)
Figure 1.4-1a
Output:
[15]
Figure 1.4-1b
Explanation to Figure 1.4-1
Python allows using underscores (_
) to make a number more readable. With non-decimal numbers (prefixed with any of these: 0x
, 0o
, 0b
), an underscore can be put after the number prefix, but a valid digit according to the number prefix has to be put after the underscore. This tells us that Python checks only for digits at the right side of an underscore. Figure 1.4-1a is therefore interpreted instead as:
a = [(0xf) or (x in (1, 2, 3))] # The hexadecimal number `0xf` is the same thing as the decimal number `15`
print(a)
Figure 1.4-2
Boolean short-circuiting (will be expanded in another chapter) results in only 0x_f
(0xf
or 15
) being evaluated, so it doesn't matter whether x
is defined or not.
Also confusing is the 'pistol operator' to assign the first and only element of a one-element list to a variable:
>>> list_obj = [42]
>>> the_meaning_of_life ,= list_obj
>>> the_meaning_of_life
42
Figure 1.5-1
Explanation to Figure 1.5-1
Python allows sortable ordered iterables as left operands for unpacking assignments, examples:
>>> (a, b) = [1, 2]
>>> a
1
>>> b
2
>>> [a, b] = [3, 4]
>>> a
3
>>> b
4
Figure 1.5-2.1a
>>> (a,) = 1
>>> a
1
>>> [a,] = 2
>>> a
2
Figure 1.5-2.1b
Comma-separated assignment—
a, b, c, d = 1, 2, 3, 4
Figure 1.5-2.2a
—just translates to—
(a, b, c, d) = 1, 2, 3, 4
Figure 1.5-2.2b
—so, Figure 1.5-1 is interpreted as:
>>> list_obj = [42]
>>> (the_meaning_of_life,) = list_obj
>>> the_meaning_of_life
42
Figure 1.5-2.3
There are also times when you can put a number right before a letter without using a space. For example:
>>> 2if 1else 3
2
Figure 1.6
There are exceptions though:
-
0o
because it's considered the prefix for an octal number
-
0x
because it's considered the prefix for a hexadecimal number
- '0b' because it's considered the prefix for a binary number
- '0x_else' because it's parsed as '0x_e lse', because
e
is part of the hexadecimal set
- '0x_and' because it's parsed as '0x_a nd', because
a
is part of the hexadecimal set
Newlines can be replaced by semicolons (;
), as in this code example:
print('a');print('b');True
Figure 1.7
But beware, scoping keywords (atleast that's what I call them, e.g. while
, if
, elif
, else
, for
, def
, and class
) cannot come after a semicolon:
print('Not valid');while True:
Figure 1.8a
File "<stdin>", line 1
print('Not valid');while True:
^^^^^
SyntaxError: invalid syntax
Figure 1.8b
While some, like import
and from
, are allowed:
>>> print('A');import math;from random import randint
A
>>> math
<module 'math' (built-in)>
>>> randint
<bound method Random.randint of <random.Random object at [hex address]>>
Figure 1.9
If you only have one level of indent within a block, you can use semicolons instead of newlines and indents. Note that any statement separated by semicolons in a one-line scope is part of the same scope.
>>> n = 5
>>> if n%4:print('Not a',end=' ');print('leap year')
Not a leap year
>>>
Figure 1.10
#Other Newline Replacements
Often, newlines can be replaced instead with a BUILD_*
expression with other expressions, like—
# Normal way
a = 3
b = 4
# Esoteric way
[a:=3, b:=4]
Figure 1.11
—or, replacing for loops—
# Normal way
d = {}
for i in range(5):
d[i] = i+1
print(i*3, d[i]*2)
# Esoteric way
[d:={},[[print(i*3,d[i]*2)for d[i]in[i+1]]for i in range(5)]] # Add `and None` in the REPL
Figure 1.12
—and the same can be done with while loops.
# Normal way
l = []
k = 500
while k != 4:
l.append(k)
if k%2:
k += 2*k+1 # Short for k=3*k+1
else:
k //= 2
print(l)
# Esoteric way
print([k:=500,list((lib:=__import__('itertools')).takewhile(lambda x:x!=4,([k, k:=(3*k+1if k%2else k//2)][0]for _ in lib.count())))][1])
Figure 1.13