Tipps für eingeschränkte Quellen in Python
Genau wie Code-Golf , restricted-Quelle schiebt eine Macke und versteckte Merkmale der Sprache Python zu nutzen. Wir haben bereits einen Platz , um alle diese zu sammeln Tipps für Code-Golf , die für restricted-Quelle bleibt mündlich weitergegeben oder versteckt tief in der Python - Dokumentation.
Daher möchte ich Sie heute fragen, was einige Tipps zur Lösung von Herausforderungen mit eingeschränkten Quellen in Python sind.
Bitte geben Sie nur 1 Tipp pro Antwort an.
Was macht hier einen guten Tipp aus?
Ich denke, ein guter Tipp sollte einige Kriterien erfüllen:
Es sollte (etwas) nicht offensichtlich sein.
Ähnlich wie bei den Code-Golf- Tipps sollte es etwas sein, an das jemand, der ein bisschen in Python Golf gespielt und die Tipps-Seite gelesen hat, nicht sofort denken würde. Zum Beispiel ist "Ersetzen
a + bdurcha+b, um Leerzeichen zu vermeiden" für jeden Golfer offensichtlich, da dies bereits eine Möglichkeit ist, Ihren Code kürzer zu machen, und daher kein guter Tipp ist.Es sollte nicht zu spezifisch sein.
Da es viele verschiedene Arten von Quellenbeschränkungen gibt, sollten die Antworten hier zumindest in gewisser Weise auf mehrere Quellenbeschränkungen oder eine gemeinsame Quellenbeschränkung anwendbar sein. Zum Beispiel sind Tipps des Formulars Wie man X ohne Verwendung von Zeichen Y macht, im Allgemeinen nützlich, da gesperrte Zeichen eine häufige Quellenbeschränkung darstellen. Das, was Ihr Tipp hilft, sollte auch etwas allgemein sein. Zum Beispiel sind Tipps des Formulars zum Erstellen von Zahlen mit X-Einschränkung hilfreich, da viele Programme unabhängig von der Herausforderung Zahlen verwenden. Tipps des Formulars Wie Sie den Shor-Algorithmus mit X-Einschränkung implementieren, sind im Grunde nur Antworten auf eine Herausforderung, die Sie gerade erfunden haben, und für Leute, die andere Herausforderungen lösen, nicht sehr hilfreich.
Antworten
Vermeiden Sie "normale" Buchstaben
Bezeichner werden vom Python 3-Parser normalisiert . Dies impliziert, dass kursive (Unicode) Buchstaben, wie sie als 𝓪𝓫𝓬𝓓𝓔𝓕ASCII-konforme Äquivalente interpretiert werden abcDEF. So folgenden Code funktioniert (wie wurde ausgebeutet hier ):
𝓝=123
𝓹𝓻𝓲𝓷𝓽(𝓝)
Python-Versionen, in denen dieses Verhalten bestätigt wird:
- Werke: 3.4, 3.5, 3.6, 3.7, 3.8
- Funktioniert nicht: 2.7
Beispiel für eine Quellenbeschränkung:
- Verwenden Sie keine Zeichen
abc···xyz,ABC···XYZ.
Vermeiden Sie Zahlen mit Booleschen Werten
Wenn Python arithmetische Operationen für Boolesche Werte ausführt, werden sie so behandelt, als wären sie die Zahlen 1 und 0. So zum Beispiel
>>> True+False
1
Sie können alle positiven Zahlen erstellen, indem Sie sich einfach Boolesche Werte hinzufügen.
Sie können auch die Boolesche Werte für Boolesche Werte ersetzen, zum Beispiel []>[]ist Falseund [[]]>[]ist Trueso
>>> ([]>[])+([[]]>[])
1
In einigen Fällen können Boolesche Werte sogar anstelle einer Zahl verwendet werden, ohne dass eine Arithmetik zum Umwandeln erforderlich ist. Sie können beispielsweise mit Booleschen Werten in Listen / Tupel / Zeichenfolgen indizieren.
>>> ['a','b'][True]
'b'
Beispiel für Quellenbeschränkungen:
Verwenden Sie keine Ziffern (
0123456789)Verwenden Sie keine alphanumerischen Zeichen
Verwenden Sie keine
ifBedingungen
Vermeiden Sie Parens mit Listenindizierung
Klammern sind sehr nützlich, um die richtige Operator-Priorität zu erstellen, sodass es ein Mist ist, wenn sie gesperrt werden. Wenn []jedoch noch verfügbar, können wir sie stattdessen verwenden. Einfach ersetzen
(...)
mit
[...][0]
Dadurch wird eine Liste erstellt und indiziert, um das einzige Element zu erhalten. Die Liste bewirkt, dass das Innere zuerst ausgewertet wird, um Ihr Vorrangproblem zu lösen.
Im obigen Beispiel werden die Zeichen verwendet []0, um dies zu tun. Es gibt jedoch andere dritte Zeichen, die in diesem Fall bei Bedarf verwendet werden können.
- Mit Zeichen
[]>schreiben[...][[]>[]] - Mit Zeichen
[]<schreiben[...][[]<[]] - Mit Zeichen
[]=schreiben[...][[[]]==[]]
Beispiel für eine Quellenbeschränkung:
- Verwenden Sie keine Klammern
Funktionsaufrufe ohne Klammern
Wir können die Verwendung von Klammern für die Operatorpriorität mithilfe der Listenindizierung vermeiden , aber Klammern sind immer noch sehr nützlich für den Aufruf von Funktionen.
Die Listenindizierung kann auch hier verwendet werden, um das Problem zu lösen. Sie ist jedoch viel komplexer, sodass ich sie zu einer eigenen Antwort gemacht habe.
Um eine Funktion aufzurufen, erstellen wir zunächst eine neue Klasse, deren Indizierung als Funktion definiert ist. Wenn wir das nennen wollen, printkönnte das so aussehen
class c:__class_getitem__=print
Um die Funktion aufzurufen, indizieren wir sie einfach mit dem gewünschten Argument. Zum Beispiel zum Drucken "Hello World"tun wir
c["Hello World"]
Dies hat einige unglückliche Mängel:
- Es kann nur verwendet werden, um Funktionen mit einem Parameter aufzurufen.
- Es gibt einige Charaktere, die erforderlich sind, um diesen Trick auszuführen. (
:=[]_acegilmst) - Es verbraucht viele Charaktere, wenn Sie Code-Golf spielen
Aber manchmal ist es vielleicht Ihre einzige Option.
Beispiel für eine Quellenbeschränkung:
- Verwenden Sie keine Klammern
Hier ist ein Beispiel dafür.
Verwenden Sie <<und |, um Konstante ohne zu generieren+
Lustige Tatsache: Sie können jede positive Konstante nur mit erhalten []<|. Der Weg ist, einen Booleschen Wert nach links zu verschieben. []<[[]]ist 1, []<[[]]<<[]<[[]]sollte also 1 mit 1 nach links verschieben, was 2 ist.
Funktioniert es?
>>> []<[[]]<<[]<[[]]
Traceback (most recent call last):
File "<pyshell#29>", line 1, in <module>
[]<[[]]<<[]<[[]]
TypeError: unsupported operand type(s) for <<: 'list' and 'list'
...Nein.
Der Vorrang ist falsch. Glücklicherweise können wir dies mit "Ad-hoc-Garf-Hunter-Klammer (TM)" beheben:
>>> [[]<[[]]][[]<[]]<<[[]<[[]]][[]<[]]
2
Aha!
Um andere Zahlen als Zweierpotenzen zu erhalten, müssen Sie noch +... oder nicht. |oder [bitwise or][[]<[]]* erledigt das für Sie.
>>> [[]<[[]]][[]<[]]<<[[]<[[]]][[]<[]]<<[[]<[[]]][[]<[]]<<[[]<[[]]][[]<[]]|[[]<[[]]][[]<[]]<<[[]<[[]]][[]<[]]
10
Um negative Zahlen ohne zu erhalten -, möchten Sie möglicherweise verwenden ~.
* Dieser Teil war in einer "Ad-hoc-Garf-Hunter-Klammer (TM)" enthalten, um den Vorrang anzuzeigen.
Zugriffsmethoden und integrierte Funktionen über __dict__
Klassen enthalten ein __dict__Attribut, das ihre Methodennamen den Methoden selbst zuordnet. Wenn Sie einen Methodennamen nicht direkt eingeben können, können Sie ihn daraus abrufen __dict__.
Angenommen, Sie müssen etwas an eine Liste anhängen, aber Sie können die Zeichen p,n,+usw. nicht verwenden . Da dies append()die 26. Methode in einer Liste ist __dict__, können Sie die Methode folgendermaßen aufrufen:
a = [1,2,3]
list(a.__class__.__dict__.values())[26](a, 4)
print(a) # prints [1,2,3,4]
Dies kann auch verwendet werden, __builtins__um auf integrierte Funktionen zuzugreifen. Selbst wenn jemand das Zeichen verbietet x, um die execFunktion zu blockieren , können Sie dennoch Folgendes aufrufen exec:
list(__builtins__.__dict__.values())[20]("print('Hello, World!')")
Dies funktioniert am besten in neueren Versionen von Python, die die Wörterbuchreihenfolge garantieren. Es gibt jedoch wahrscheinlich andere Möglichkeiten, dies in älteren Versionen zu verwenden, z. B. das Durchlaufen der __dict__mit einer Regex- oder Teilzeichenfolgenübereinstimmung.
Verwenden Sie ord()oder binäre Zeichenfolgen, um Ziffern zu vermeiden
Die meisten Ganzzahlen in den Bereichen [32..47]und [58..126]können leicht aus dem ASCII-Code eines einzelnen Zeichens erhalten werden mit:
x=ord('A')
# or, if parentheses are not allowed:
y=b'A'[False]
Größere Ganzzahlen können auch mit ihren Unicode-Punkten erzeugt werden:
>>>print (ord("±"))
177
>>> print (ord("π"))
960
Wenn Sie eine Zuweisung verwenden können oder Klammern vermeiden müssen, können Sie stattdessen die Werte entpacken. Beachten Sie, dass dies auch mit dem Walross-Operator nicht inline funktioniert.
x,*_=b'A'
y,_=b'A_'
Beispiel für Quellenbeschränkungen:
- Verwenden Sie keine Ziffern
- Verwenden Sie keine Ziffern / keine Klammern / keine Klammern
Verwenden Sie --, um + zu vermeiden
ZB zu tun a+b:
a--b
Beispiel für eine Quellenbeschränkung:
- Vermeiden Sie den
+Bediener
Alternativen zu evalundexec
Sie benötigen einen String als Code zu behandeln, aber sie können nicht evaloder exec? Es gibt mindestens drei weitere Möglichkeiten, eine Zeichenfolge auszuführen:
1) timeit.timeit
import timeit
_=timeit.timeit("print('Hello!')", number=1)
timeitLäuft numbermal und gibt durchschnittlich zurück, wie lange es gedauert hat. Standardmäßig wird es 1 Million Mal ausgeführt, daher möchten Sie mit ziemlicher Sicherheit number=1eine Ausnahme festlegen oder auslösen, um auszubrechen (z "print('hello'); 0/0". B. ).
Vielen Dank an Ethan White , der mir diesen Ansatz gezeigt hat.
2) os.system
import os
c='echo "import math;print(math.pi)" | python3'
_=os.system(c) # Prints 3.141592653589793
os.systemführt einen beliebigen Shell-Befehl aus und gibt seinen Exit-Code zurück. Wenn Sie nur etwas drucken müssen, können Sie sich daran halten echo, aber Sie können auch beliebigen Code ausführen, indem Sie sich python3selbst aufrufen .
3) code.InteractiveInterpreter (). Runcode
from code import InteractiveInterpreter as I
i = I()
i.runcode("print('Hello!')")
codewurde speziell für Read-Eval-Print-Schleifen entwickelt, und obwohl es etwas klobig ist, ist dies die leistungsstärkste der drei. timeitund os.systemisolieren ihre Prozesse, aber ein InteractiveInterpreterkann globalen Zustand anstelle seines eigenen verwenden:
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
Verwenden Sie *, um zu vermeiden/
x**-1ist äquivalent zu 1/x. Also y/xkannst du es tun x**-1*y.
Wenn Sie das unbedingt loswerden möchten, lesen -1Sie den anderen Tipp von Ad Hoc Garf Hunter.
Beispiel für eine Quellenbeschränkung:
- Vermeiden Sie die Verwendung des
/Zeichens
Beschränken Sie eingeschränkte Zeichen und verwenden Sie sie exec()
Wie die meisten interpretierten Sprachen kann Python eine Zeichenfolge als Code mit evalund ausführen exec. evalist eingeschränkter, execkann jedoch Importe, Funktionsdefinitionen, Schleifen, Ausnahmen usw. verarbeiten.
In Kombination mit einigen anderen Tipps zum Codieren von Zeichen können Sie Ihren Code normal schreiben:
import sys
def f(i):
return 1 if i==1 else i*f(i-1)
i=int(sys.argv[1])
print(f(i))
Wählen Sie dann eine Codierung aus und übergeben Sie die codierte Version an 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')
Ersetzen Sie Operatoren durch Dunder-Methoden
Die meisten Python-Operatoren sind syntaktischer Zucker für bestimmte Methodenaufrufe (oft als "magische Methoden" oder "Dunder-Methoden" bezeichnet, wobei "Dunder" für "doppelter Unterstrich" steht). Zum Beispiel +Anrufe __add__(), ==Anrufe __eq__()und <<Anrufe__lshift__()
Wenn Operatoren eingeschränkt sind, können Sie diese Methoden direkt aufrufen:
a = 1
print(a.__add__(1).__eq__(2)) # True
Für die Zuweisung können Sie __setitem__in den Wörterbüchern locals()oder verwenden globals(), ob die Variable bereits vorhanden ist oder nicht:
a = 1
locals().__setitem__('a',2)
locals().__setitem__('b',2)
print(a.__add__(b).__eq__(4)) # True
Beachten Sie, dass Sie Zahlen um Klammern setzen müssen, um einen Syntaxfehler zu vermeiden. 4.__eq__(4)wird nicht funktionieren, wird aber (4).__eq__(4).
So erstellen Sie Zeichen nur mit Zahlen, Anführungszeichen und Backslashes
Strings können zusammengesetzt werden , \ooowo oooder oktalen Wert des Zeichens.
Z.B:
'\141'=='a'
Sie können auch hex, auf Kosten eines verwenden x(und a, b, c, d, eund / oder fwenn sie verwendet wird ):
'\x61'=='a'
Und Unicode auf Kosten von a u(zwei Pre-Python 3) und Hex-Zeichen, wenn sie verwendet werden:
'\u2713'=='✓'
Verwenden Sie __import__("module")anstelle vonimport module
Um Leerzeichen in der zu vermeiden import statementoder den Namen des zu importierenden Moduls dynamisch als Zeichenfolge zu erstellen (z. B. "RANDOM".lower()wenn Sie keine Kleinbuchstaben verwenden können d). Nicht wahrscheinlich , dass nützlich , weil man nicht oft die Standard - Bibliothek benötigen, und Sie immer noch verwenden müssen in der Lage sein _, i, m, p, o, r, t, (, und ).
Bearbeiten: oder wie Ad Hoc Garf Hunter vorschlägt, können Sie verwenden import<tab>module(mit einem wörtlichen Tabulatorzeichen)!
Dies ist wahrscheinlich nicht relevant für die Frage, aber es ist sehr dumm.
Dies ist (möglicherweise) eine tragbarere Methode für die verschlungene Methode von water_ghost, auf integrierte Methoden zuzugreifen.
Der Index ist 26 nur unter CPython 3. Diese sehr kleine und äußerst leicht verständliche Änderung ermöglicht die Ausführung unter CPython 2.7, CPython 3, PyPy 2.7 und PyPy 3 (getestet unter 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)
Die richtigen Indizes (auf meinem System) sind
CPython 2.7 13
CPython 3 26
PyPy 2.7 26
PyPy 3 25
Und durch einen glücklichen Zufall ('C'+'2')%4 == 1, ('C'+'3')%4 == 2, ('P'+'2') == 2und ('P'+'3') == 3. Der Wert 14 soll Sie dazu verleiten, zu glauben, dass es ein Muster gibt.
__debug__ ist wahr
Ziemlich selbsterklärend ... expl ...
>>> __debug__
True