33
\$\begingroup\$

Just like , pushes one to exploit quirks and hidden features of the Python language. We already have a place to collect all these for , those for remain transmitted by word of mouth or hidden deep within the python documentation.

So today I would like to ask you what are some for solving challenges in Python?

Please only include 1 tip per answer.


What makes a good tip here?

There are a couple of criteria I think a good tip should have:

  1. It should be (somewhat) non obvious.

    Similar to the tips it should be something that someone who has golfed in python a bit and read the tips page would not immediately think of. For example "Replace a + b with a+b to avoid using spaces", is obvious to any golfer since it is already a way to make your code shorter and thus not a good tip.

  2. It should not be too specific.

    Since there are many different types of source restrictions, answers here should be at least somewhat applicable to multiple source restrictions, or one common source restriction. For example tips of the form How to X without using character(s) Y are generally useful since banned characters is a common source restriction. The thing your tip helps to do should also be somewhat general. For example tips of the form How to create numbers with X restriction are useful since many programs utilize numbers regardless of the challenge. Tips of the form How to implement Shor's algorithm with X restriction are basically just answers to a challenge you just invented and not very helpful to people solving other challenges.

\$\endgroup\$
  • 1
    \$\begingroup\$ This and this. \$\endgroup\$ – nope Aug 18 at 14:46
  • 1
    \$\begingroup\$ I think you can avoid == and ==0 with the ^ and ^0 operations. Does this count as a wat for doing restricted source? \$\endgroup\$ – user96495 Aug 19 at 1:55
  • 3
    \$\begingroup\$ Reading the answers, but pretending you are on codereview.SE is good entertainment. \$\endgroup\$ – SE - stop firing the good guys Aug 21 at 19:05
  • 1
    \$\begingroup\$ Make use of answers from the Fewest (distinct) characters for Turing Completeness challenge. Another solution \$\endgroup\$ – mbomb007 Aug 21 at 21:52

16 Answers 16

24
\$\begingroup\$

Avoid "normal" letters

Identifiers are normalized by the Python 3 parser. This implies that cursive (Unicode) letters such as 饾摢饾摣饾摤饾摀饾摂饾摃 are interpreted as their ASCII-compliant equivalents abcDEF. So the following code works (as was exploited here):

饾摑=123
饾摴饾摶饾摬饾摲饾摻(饾摑)

Python versions in which this behaviour is confirmed:

  • Works: 3.4, 3.5, 3.6, 3.7, 3.8
  • Doesn't work: 2.7

Example source restriction:

  • Use no characters abc路路路xyz, ABC路路路XYZ.
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Worth noting that this only works in some versions of Python. \$\endgroup\$ – Wheat Wizard Aug 18 at 14:58
  • \$\begingroup\$ @AdHocGarfHunter Is it only version 3? Or which versions? Feel free to edit the answer \$\endgroup\$ – Luis Mendo Aug 18 at 15:29
  • \$\begingroup\$ It started sometime in Python 3. I don't no precisely when. \$\endgroup\$ – Wheat Wizard Aug 18 at 15:52
  • 1
    \$\begingroup\$ @AdHocGarfHunter Anyway, this will always work in Python 3.7 \$\endgroup\$ – user96495 Aug 19 at 8:06
  • 1
    \$\begingroup\$ @ChrisH Thank you! I have included this information in the answer \$\endgroup\$ – Luis Mendo Aug 21 at 11:43
20
\$\begingroup\$

Avoid Numbers with Booleans

When performing arithmetic operations on Booleans, Python treats them as if they are the numbers 1 and 0. So for example

>>> True+False
1

You can make all positive numbers by just adding booleans to each other.

You can also substitute the booleans for boolean values, for example []>[] is False and [[]]>[] is True so

>>> ([]>[])+([[]]>[])
1

In some cases, Booleans can even be used in place of a number without having to using arithmetic to cast it. For example, you can index into lists/tuples/strings with Booleans, so:

>>> ['a','b'][True]
'b'

Example source restrictions:

  • Use no digits (0123456789)

  • Use no alphanumeric characters

  • Use no if conditions

\$\endgroup\$
18
\$\begingroup\$

Avoid Parens with List indexing

Parentheses are super useful for creating the correct operator precedence so it is a bummer when they are banned. However if [] are still available we can use them instead. Simply replace

(...)

with

[...][0]

This creates a list and indexes it to get its only element. The list causes the inside to be evaluated first solving your precedence issue.

The example above uses the characters []0 to do this however there are other third characters that can be used in this case if need be.

  • Using characters []> write [...][[]>[]]
  • Using characters []< write [...][[]<[]]
  • Using characters []= write [...][[[]]==[]]

Example source restriction:

  • Use no parentheses
\$\endgroup\$
15
\$\begingroup\$

Function calls without parentheses

We can avoid using parentheses for operator precedence using list indexing, but parentheses are still very useful for calling functions.

List indexing can be used here as well to solve the issue, however it is a lot more complex so I've made it its own answer.

In order to call a function we start by making a new class which has it's indexing defined to be the function. So if we want to call print this might look like

class c:__class_getitem__=print

Then to call the function we simply index it with the argument we want. For example to print "Hello World" we do

c["Hello World"]

This has a few unfortunate short comings:

  • It can only be used to call functions with one parameter.
  • There are quite a few characters that are required to pull this trick off. ( :=[]_acegilmst)
  • It uses up a lot of characters if you are doing

But sometimes it might be your only option.


Example source restriction:

  • Use no parentheses

Here's an example of it being used.

| improve this answer | |
\$\endgroup\$
13
\$\begingroup\$

Use << and | to generate constant without +

Fun fact: You can get any positive constant only using []<|. The way to go is left-shifting a boolean. []<[[]] is 1, so []<[[]]<<[]<[[]] should left-shift 1 with 1, which is 2.

Does it work?

>>> []<[[]]<<[]<[[]]

Traceback (most recent call last):
  File "<pyshell#29>", line 1, in <module>
    []<[[]]<<[]<[[]]
TypeError: unsupported operand type(s) for <<: 'list' and 'list'

...No.

The precedence is wrong. Luckily, we can resolve this with "Ad Hoc Garf Hunter Parenthesis(TM)":

>>> [[]<[[]]][[]<[]]<<[[]<[[]]][[]<[]]
2

Aha!

To get numbers other than power of two, you still need +... or not. | or [bitwise or][[]<[]]* will do that for you.

>>> [[]<[[]]][[]<[]]<<[[]<[[]]][[]<[]]<<[[]<[[]]][[]<[]]<<[[]<[[]]][[]<[]]|[[]<[[]]][[]<[]]<<[[]<[[]]][[]<[]]
10

To get negative numbers without -, you may want to use ~.

* That part was enclosed in an "Ad Hoc Garf Hunter Parenthesis(TM)" to indicate precedence.

| improve this answer | |
\$\endgroup\$
12
\$\begingroup\$

Use ord() or binary strings to avoid digits

Most integers in the ranges [32..47] and [58..126] can easily be obtained from the ASCII code of a single character with:

x=ord('A')

# or, if parentheses are not allowed:

y=b'A'[False]

Try it online!

Larger integers can also be produced using from their unicode points:

>>>print (ord("卤"))
177
>>> print (ord("蟺"))
960

If you can use an assignment, or you need to avoid brackets you can unpack the values, instead. Note that this will not work inline, even with the walrus operator.

x,*_=b'A'
y,_=b'A_'

Try it online!

Example source restrictions:

  • Use no digits
  • Use no digits / no parentheses / no brackets
\$\endgroup\$
11
\$\begingroup\$

Access methods and built-in functions through __dict__

Classes contain a __dict__ attribute, which maps their method names to the methods themselves. If you can't type a method name directly, you can get them from this __dict__.

For example, let's say you need to append something to a list, but you can't use the characters p,n,+, etc. Since append() is the 26th method in a list's __dict__, you can call the method like this:

a = [1,2,3]
list(a.__class__.__dict__.values())[26](a, 4)
print(a)  # prints [1,2,3,4]

Try it online!

This can also be used with __builtins__ to access built-in functions. Even if someone bans the character x to block the exec function, you can still call exec like this:

list(__builtins__.__dict__.values())[20]("print('Hello, World!')")

Try it online!

This works best in more recent versions of Python that guarantee dictionary order, but there are probably other ways to use this in older versions, like iterating through the __dict__ with a regex or substring match.

| improve this answer | |
\$\endgroup\$
  • 2
    \$\begingroup\$ This is so convoluted and ridiculous. I love it. \$\endgroup\$ – Wheat Wizard Aug 19 at 0:15
  • 1
    \$\begingroup\$ The index is 13 in Python 2.7. Or (ord(sys.version[0])*13-637) \$\endgroup\$ – Oskar Skog Aug 21 at 15:27
8
\$\begingroup\$

Use -- to avoid +

E.g to do a+b:

a--b

Example source restriction:

  • Avoid the + operator
| improve this answer | |
\$\endgroup\$
6
\$\begingroup\$

Alternatives to eval and exec

Do you need to treat a string as code, but you can't use eval or exec? There are at least three other ways to execute a string:

1) timeit.timeit

import timeit
_=timeit.timeit("print('Hello!')", number=1)

Try it online!

timeit runs number times and returns an average of how long it took. By default it runs 1 million times, so you'll almost certainly want to set number=1 or raise an exception to break out (e.g. "print('hello'); 0/0").

Thanks to Ethan White for showing me this approach.

2) os.system

import os
c='echo "import math;print(math.pi)" | python3'
_=os.system(c)  # Prints 3.141592653589793

Try it online!

os.system runs an arbitrary shell command and returns its exit code. If you just need to print something, you can stick to echo, but you can also execute arbitrary code by calling python3 itself.

3) code.InteractiveInterpreter().runcode

from code import InteractiveInterpreter as I
i = I()
i.runcode("print('Hello!')")

Try it online!

code is specifically designed for read-eval-print loops, and even though it's a bit clunky, this is the most powerful of the three. timeit and os.system isolate their processes, but an InteractiveInterpreter can use global state instead of its own:

from code import InteractiveInterpreter as I
a = 64
i = I(globals())
i.runcode("import math; a=math.log2(a)")
print(a)        # a = 6.0
print(math.pi)  # math is imported globally

Try it online!

| improve this answer | |
\$\endgroup\$
4
\$\begingroup\$

Use * to avoid /

x**-1 is equivalent to 1/x. So to do y/x you can do x**-1*y.

If you despararely want to get rid of the -1, you can check out Ad Hoc Garf Hunter's other tip.

Example source restriction:

  • Avoid using the / character
| improve this answer | |
\$\endgroup\$
4
\$\begingroup\$

Encode restricted characters and use exec()

Like most interpreted languages, Python can run a string as code with eval and exec. eval is more limited, but exec can handle imports, function definitions, loops, exceptions, etc.

When combined with some of the other tips about encoding characters, this allows you to write your code normally:

import sys

def f(i):
 return 1 if i==1 else i*f(i-1)

i=int(sys.argv[1])
print(f(i))
    

Then pick an encoding and pass the encoded version to exec:

exec('\x69\x6d\x70\x6f\x72\x74\x20\x73\x79\x73\x0a\x0a\x64\x65\x66\x20\x66\x28\x69\x29\x3a\x0a\x20\x72\x65\x74\x75\x72\x6e\x20\x31\x20\x69\x66\x20\x69\x3d\x3d\x31\x20\x65\x6c\x73\x65\x20\x69\x2a\x66\x28\x69\x2d\x31\x29\x0a\x0a\x69\x3d\x69\x6e\x74\x28\x73\x79\x73\x2e\x61\x72\x67\x76\x5b\x31\x5d\x29\x0a\x70\x72\x69\x6e\x74\x28\x66\x28\x69\x29\x29\x0a')

Try it online!

| improve this answer | |
\$\endgroup\$
4
\$\begingroup\$

Replace operators with dunder methods

Most python operators are syntactic sugar for specific method calls (often called "magic methods" or "dunder methods", with "dunder" being short for "double underscore"). For example, + calls __add__(), == calls __eq__(), and << calls __lshift__()

If operators are restricted, you can call these methods directly:

a = 1
print(a.__add__(1).__eq__(2))  # True

Try it online!

For assignment, you can use __setitem__ on the locals() or globals() dictionaries, whether or not the variable already exists:

a = 1
locals().__setitem__('a',2)
locals().__setitem__('b',2)
print(a.__add__(b).__eq__(4))  # True

Try it online!

Note that you'll have to add parentheses around numbers to avoid a syntax error. 4.__eq__(4) won't work, but (4).__eq__(4) will.

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ 4 .__eq__(4) also works. \$\endgroup\$ – pppery Aug 19 at 16:38
  • \$\begingroup\$ aha! Got it! Then my method of avoiding + is completely nonsense. \$\endgroup\$ – null Aug 20 at 1:43
3
\$\begingroup\$

How to create characters just with numbers, quotes and backslashes

Strings can be composed with \ooo where ooo is the octal value of the character.

Eg:

'\141'=='a'

You can also use hex, at the expense of an x (and a,b,c,d,e and/or f if they're used):

'\x61'=='a'  

And unicode at the expense of a u (two pre-Python 3) and hex characters if they're used:

'\u2713'=='鉁'
\$\endgroup\$
3
\$\begingroup\$

Use __import__("module") instead of import module

To avoid whitespace in the import statement, or to dynamically construct the name of the module to import as a string (e.g. "RANDOM".lower() if you can't use a lowercase d). Not likely that useful because you don't often need the standard library, and you still need to be able to use _, i, m, p, o, r, t, (, and ).

Edit: or as Ad Hoc Garf Hunter suggests, you can use import<tab>module (with a literal tab character)!

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ It might be worth mentioning, if you only need to avoid spaces you can just use a tab between import and the module name. \$\endgroup\$ – Wheat Wizard Aug 21 at 17:31
  • \$\begingroup\$ You can also assign the return value to an alias. For example, if you can't type a, you could use m=__import__('MATH'.lower()); print(m.pi) \$\endgroup\$ – water_ghosts Aug 21 at 19:14
  • \$\begingroup\$ @AdHocGarfHunter Pretty sure Python also accepts the "vertical tab" character. \$\endgroup\$ – mbomb007 Aug 21 at 21:50
2
\$\begingroup\$

This is likely not relevant to the question but it's very silly.

This is (maybe) a more portable way of water_ghost's convoluted method of accessing builtin methods.
The index is 26 only on CPython 3. This very small and extremely easy to understand modification allows it to run on CPython 2.7, CPython 3, PyPy 2.7 and PyPy 3 (tested on Debian 10 amd64)

a = [1, 2, 3]
list(a.__class__.__dict__.values())[[14,13,26,25][sum(map(ord,{__import__("sys").version[0],__import__("platform").python_implementation()[0]}))&3]](a, 4)
print(a)

The correct indices (on my system) are

CPython 2.7    13
CPython 3      26
PyPy 2.7       26
PyPy 3         25

And by a lucky coincidence ('C'+'2')%4 == 1, ('C'+'3')%4 == 2, ('P'+'2') == 2 and ('P'+'3') == 3. The value 14 is there to trick you into thinking there is a pattern.

| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

__debug__ is True

Pretty self-expl... expl...

>>> __debug__
True
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Ha! I figured out how to gain rep! Just post random comment-like answers everywhere! \$\endgroup\$ – null Aug 27 at 11:38

Your Answer

By clicking 鈥淧ost Your Answer鈥, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.