Tips untuk sumber terbatas dengan Python

Aug 18 2020

Sama seperti kode-golf , sumber-terbatas mendorong seseorang untuk mengeksploitasi kebiasaan dan fitur tersembunyi dari bahasa Python. Kami sudah memiliki tempat untuk mengumpulkan semua tip ini untuk kode-golf , yang untuk sumber terbatas tetap dikirimkan dari mulut ke mulut atau tersembunyi jauh di dalam dokumentasi python.

Jadi hari ini saya ingin bertanya kepada Anda apa saja tip untuk menyelesaikan tantangan sumber terbatas dengan Python?

Harap hanya sertakan 1 tip per jawaban.


Apa yang membuat tip bagus di sini?

Ada beberapa kriteria yang menurut saya harus dimiliki tip yang bagus:

  1. Ini harus (agak) tidak jelas.

    Mirip dengan tips kode-golf, itu harus menjadi sesuatu yang seseorang yang telah sedikit bermain golf dengan python dan membaca halaman tips tidak akan langsung memikirkannya. Misalnya "Ganti a + bdengan a+buntuk menghindari penggunaan spasi", jelas bagi pegolf mana pun karena ini merupakan cara untuk membuat kode Anda lebih pendek dan dengan demikian bukan tip yang baik.

  2. Seharusnya tidak terlalu spesifik.

    Karena ada banyak jenis batasan sumber, jawaban di sini setidaknya dapat diterapkan pada beberapa batasan sumber, atau satu batasan sumber umum. Misalnya tip formulir Cara X tanpa menggunakan karakter Y umumnya berguna karena karakter yang dilarang adalah batasan sumber yang umum. Hal yang membantu tip Anda juga harus bersifat umum. Sebagai contoh tip formulir Cara membuat angka dengan batasan X berguna karena banyak program menggunakan angka terlepas dari tantangannya. Tip dalam bentuk Bagaimana mengimplementasikan algoritma Shor dengan batasan X pada dasarnya hanyalah jawaban atas tantangan yang baru saja Anda temukan dan tidak terlalu membantu orang untuk memecahkan tantangan lain.

Jawaban

24 LuisMendo Aug 18 2020 at 21:45

Hindari huruf "normal"

Pengenal dinormalisasi oleh parser Python 3. Ini menyiratkan bahwa huruf kursif (Unicode) seperti 𝓪𝓫𝓬𝓓𝓔𝓕ditafsirkan sebagai padanan yang sesuai dengan ASCII abcDEF. Jadi kode berikut berfungsi (seperti yang dieksploitasi di sini ):

𝓝=123
𝓹𝓻𝓲𝓷𝓽(𝓝)

Versi Python di mana perilaku ini dikonfirmasi:

  • Pekerjaan: 3.4, 3.5, 3.6, 3.7, 3.8
  • Tidak bekerja: 2.7

Contoh pembatasan sumber:

  • Tidak menggunakan karakter abc···xyz, ABC···XYZ.
21 AdHocGarfHunter Aug 18 2020 at 21:02

Hindari Angka dengan Boolean

Saat melakukan operasi aritmatika pada Boolean, Python memperlakukannya seolah-olah itu adalah angka 1 dan 0. Jadi misalnya

>>> True+False
1

Anda dapat membuat semua bilangan positif hanya dengan menambahkan boolean satu sama lain.

Anda juga dapat mengganti boolean untuk nilai boolean, misalnya []>[]adalah Falsedan [[]]>[]adalah Truebegitu

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

Dalam beberapa kasus, Boolean bahkan dapat digunakan sebagai pengganti angka tanpa harus menggunakan aritmatika untuk mentransmisikannya. Misalnya, Anda dapat mengindeks ke dalam daftar / tupel / string dengan Boolean, jadi:

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

Contoh batasan sumber:

  • Gunakan tanpa digit ( 0123456789)

  • Jangan gunakan karakter alfanumerik

  • Gunakan tanpa ifsyarat

19 AdHocGarfHunter Aug 18 2020 at 21:12

Hindari Parens dengan pengindeksan Daftar

Tanda kurung sangat berguna untuk membuat prioritas operator yang benar sehingga akan mengecewakan saat dilarang. Namun jika []masih tersedia kita bisa menggunakannya. Cukup ganti

(...)

dengan

[...][0]

Ini membuat daftar dan mengindeksnya untuk mendapatkan satu-satunya elemennya. Daftar tersebut menyebabkan bagian dalam dievaluasi terlebih dahulu untuk memecahkan masalah prioritas Anda.

Contoh di atas menggunakan karakter []0untuk melakukan ini, namun ada karakter ketiga lainnya yang dapat digunakan dalam kasus ini jika perlu.

  • Menggunakan karakter []>menulis[...][[]>[]]
  • Menggunakan karakter []<menulis[...][[]<[]]
  • Menggunakan karakter []=menulis[...][[[]]==[]]

Contoh pembatasan sumber:

  • Gunakan tanpa tanda kurung
16 WheatWizard Aug 18 2020 at 22:16

Panggilan fungsi tanpa tanda kurung

Kita dapat menghindari penggunaan tanda kurung untuk prioritas operator menggunakan pengindeksan daftar , tetapi tanda kurung masih sangat berguna untuk memanggil fungsi.

Pengindeksan daftar dapat digunakan di sini juga untuk menyelesaikan masalah, namun ini jauh lebih kompleks, jadi saya membuatnya menjadi jawabannya sendiri.

Untuk memanggil suatu fungsi, kita mulai dengan membuat kelas baru yang pengindeksannya didefinisikan sebagai fungsi. Jadi jika kita ingin menyebutnya printmungkin akan terlihat seperti ini

class c:__class_getitem__=print

Kemudian untuk memanggil fungsi tersebut kita cukup mengindeksnya dengan argumen yang kita inginkan. Misalnya untuk mencetak yang "Hello World"kita lakukan

c["Hello World"]

Ini memiliki beberapa kekurangan yang tidak menguntungkan:

  • Ini hanya dapat digunakan untuk memanggil fungsi dengan satu parameter.
  • Ada beberapa karakter yang dibutuhkan untuk melakukan trik ini. ( :=[]_acegilmst)
  • Ini menggunakan banyak karakter jika Anda melakukan kode-golf

Tapi terkadang itu mungkin satu-satunya pilihan Anda.


Contoh pembatasan sumber:

  • Gunakan tanpa tanda kurung

Berikut adalah contoh penggunaannya.

14 null Aug 19 2020 at 07:59

Gunakan <<dan |untuk menghasilkan konstanta tanpa+

Fakta menarik: Anda bisa mendapatkan konstanta positif hanya dengan menggunakan []<|. Cara untuk melakukannya adalah menggeser boolean ke kiri. []<[[]]adalah 1, jadi []<[[]]<<[]<[[]]harus menggeser ke kiri 1 dengan 1, yaitu 2.

Apakah itu bekerja?

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

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

...Tidak.

Prioritasnya salah. Untungnya, kami dapat menyelesaikan ini dengan "Ad Hoc Garf Hunter Parenthesis (TM)":

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

Aha!

Untuk mendapatkan angka selain pangkat dua, Anda masih perlu +... atau tidak. |atau [bitwise or][[]<[]]* akan melakukannya untuk Anda.

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

Untuk mendapatkan bilangan negatif tanpa -, Anda mungkin ingin menggunakan ~.

* Bagian itu diapit oleh "Ad Hoc Garf Hunter Parenthesis (TM)" untuk menunjukkan prioritas.

12 water_ghosts Aug 19 2020 at 06:45

Metode akses dan fungsi bawaan melalui __dict__

Kelas berisi __dict__atribut, yang memetakan nama metode mereka ke metode itu sendiri. Jika Anda tidak dapat mengetik nama metode secara langsung, Anda bisa mendapatkannya dari ini __dict__.

Misalnya, Anda perlu menambahkan sesuatu ke daftar, tetapi Anda tidak dapat menggunakan karakter p,n,+, dll. Karena append()merupakan metode ke-26 dalam daftar __dict__, Anda dapat memanggil metode seperti ini:

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

Cobalah secara online!

Ini juga dapat digunakan dengan __builtins__untuk mengakses fungsi bawaan. Meskipun seseorang melarang karakter tersebut xuntuk memblokir execfungsinya, Anda tetap dapat memanggil execseperti ini:

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

Cobalah secara online!

Ini bekerja paling baik di versi Python yang lebih baru yang menjamin urutan kamus, tetapi mungkin ada cara lain untuk menggunakan ini di versi yang lebih lama, seperti iterasi melalui __dict__dengan pencocokan ekspresi reguler atau substring.

12 Arnauld Aug 18 2020 at 21:59

Gunakan ord()atau string biner untuk menghindari angka

Sebagian besar bilangan bulat dalam rentang [32..47]dan [58..126]dapat dengan mudah diperoleh dari kode ASCII dari satu karakter dengan:

x=ord('A')

# or, if parentheses are not allowed:

y=b'A'[False]

Cobalah secara online!

Bilangan bulat yang lebih besar juga dapat diproduksi menggunakan dari titik unicode mereka:

>>>print (ord("±"))
177
>>> print (ord("π"))
960

Jika Anda dapat menggunakan tugas, atau Anda perlu menghindari tanda kurung, Anda dapat membongkar nilainya. Perhatikan bahwa ini tidak akan bekerja sebaris, bahkan dengan operator walrus.

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

Cobalah secara online!

Contoh batasan sumber:

  • Gunakan tidak ada angka
  • Gunakan tanpa digit / tanpa tanda kurung / tanpa tanda kurung
9 Noname Aug 18 2020 at 21:31

Gunakan --untuk menghindari +

Misalnya untuk melakukan a+b:

a--b

Contoh pembatasan sumber:

  • Hindari +operator
6 water_ghosts Aug 21 2020 at 12:19

Alternatif untuk evaldanexec

Apakah Anda perlu memperlakukan string sebagai kode, tetapi Anda tidak dapat menggunakan evalatau exec? Setidaknya ada tiga cara lain untuk mengeksekusi string:

1) timeit.timeit

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

Cobalah secara online!

timeitberjalan numberkali dan mengembalikan rata-rata berapa lama waktu yang dibutuhkan. Secara default, ini berjalan 1 juta kali, jadi Anda hampir pasti ingin menyetel number=1atau memunculkan pengecualian untuk keluar (mis "print('hello'); 0/0".).

Terima kasih kepada Ethan White karena telah menunjukkan pendekatan ini kepada saya.

2) sistem operasi

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

Cobalah secara online!

os.systemmenjalankan perintah shell sewenang-wenang dan mengembalikan kode keluarnya. Jika Anda hanya perlu mencetak sesuatu, Anda dapat tetap menggunakannya echo, tetapi Anda juga dapat mengeksekusi kode arbitrer dengan memanggil python3dirinya sendiri.

3) code.InteractiveInterpreter (). Runcode

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

Cobalah secara online!

codedirancang khusus untuk loop read-eval-print, dan meskipun agak kikuk, ini adalah yang paling kuat dari ketiganya. timeitdan os.systemmengisolasi prosesnya, tetapi InteractiveInterpreterdapat menggunakan status global, bukan miliknya sendiri:

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

Cobalah secara online!

4 Noname Aug 18 2020 at 21:41

Gunakan *untuk menghindari/

x**-1setara dengan 1/x. Jadi apa y/xyang bisa Anda lakukan x**-1*y.

Jika Anda sangat ingin menyingkirkannya -1, Anda dapat melihat tip Ad Hoc Garf Hunter lainnya.

Contoh pembatasan sumber:

  • Hindari menggunakan /karakter
4 water_ghosts Aug 19 2020 at 06:19

Menyandikan karakter dan penggunaan yang dibatasi exec()

Seperti kebanyakan bahasa yang ditafsirkan, Python dapat menjalankan string sebagai kode dengan evaldan exec. evallebih terbatas, tetapi execdapat menangani impor, definisi fungsi, loop, pengecualian, dll.

Jika digabungkan dengan beberapa tip lain tentang encoding karakter, ini memungkinkan Anda untuk menulis kode Anda secara normal:

import sys

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

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

Kemudian pilih encoding dan teruskan versi yang dienkodekan ke 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')

Cobalah secara online!

4 water_ghosts Aug 19 2020 at 13:38

Ganti operator dengan metode dunder

Kebanyakan operator python adalah gula sintaksis untuk panggilan metode tertentu (sering disebut "metode ajaib" atau "metode dunder", dengan "dunder" merupakan kependekan dari "garis bawah ganda"). Misalnya +panggilan __add__(), ==panggilan __eq__(), dan <<panggilan__lshift__()

Jika operator dibatasi, Anda dapat memanggil metode ini secara langsung:

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

Cobalah secara online!

Untuk tugas, Anda dapat menggunakan __setitem__di locals()atau globals()kamus, apakah variabel sudah ada:

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

Cobalah secara online!

Perhatikan bahwa Anda harus menambahkan tanda kurung di sekitar angka untuk menghindari kesalahan sintaks. 4.__eq__(4)tidak akan berhasil, tapi (4).__eq__(4)akan.

3 Noodle9 Aug 18 2020 at 21:26

Cara membuat karakter hanya dengan angka, tanda kutip dan garis miring terbalik

String dapat terdiri dengan \ooomana oooadalah nilai oktal karakter.

Misalnya:

'\141'=='a'

Anda juga dapat menggunakan hex, dengan mengorbankan sebuah x(dan a, b, c, d, edan / atau fjika mereka digunakan):

'\x61'=='a'  

Dan unicode dengan mengorbankan u(dua pra-Python 3) dan karakter hex jika digunakan:

'\u2713'=='✓'
3 pxeger Aug 21 2020 at 20:11

Gunakan __import__("module")sebagai gantiimport module

Untuk menghindari spasi kosong di import statement, atau untuk membangun nama modul secara dinamis untuk diimpor sebagai string (misalnya "RANDOM".lower()jika Anda tidak dapat menggunakan huruf kecil d). Tidak mungkin bahwa berguna karena Anda tidak sering perlu perpustakaan standar, dan Anda masih perlu untuk dapat digunakan _, i, m, p, o, r, t, (, dan ).

Sunting: atau seperti yang disarankan Ad Hoc Garf Hunter, Anda dapat menggunakan import<tab>module(dengan karakter tab literal)!

2 OskarSkog Aug 21 2020 at 22:57

Ini mungkin tidak relevan dengan pertanyaan tetapi sangat konyol.

Ini (mungkin) cara yang lebih portabel dari metode rumit water_ghost untuk mengakses metode bawaan.
Indeks hanya 26 pada CPython 3. Modifikasi yang sangat kecil dan sangat mudah dimengerti ini memungkinkannya berjalan pada CPython 2.7, CPython 3, PyPy 2.7 dan PyPy 3 (diuji pada 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)

Indeks yang benar (di sistem saya) adalah

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

Dan oleh kebetulan beruntung ('C'+'2')%4 == 1, ('C'+'3')%4 == 2, ('P'+'2') == 2dan ('P'+'3') == 3. Nilai 14 ada untuk menipu Anda agar berpikir bahwa ada pola.

2 null Aug 27 2020 at 18:30

__debug__ adalah benar

Cukup self-expl ... expl ...

>>> __debug__
True