เคล็ดลับสำหรับแหล่งที่มาที่ จำกัด ใน Python

Aug 18 2020

เช่นเดียวกับรหัสกอล์ฟ , จำกัด แหล่งที่มาผลักดันหนึ่งที่จะใช้ประโยชน์จากมาตรฐานและคุณลักษณะที่ซ่อนอยู่ของภาษา Python เรามีสถานที่สำหรับรวบรวมเคล็ดลับเหล่านี้ทั้งหมดสำหรับการเล่นโค้ดกอล์ฟแล้วคำแนะนำสำหรับแหล่งที่มาที่ จำกัดยังคงถูกถ่ายทอดโดยปากต่อปากหรือซ่อนอยู่ลึก ๆ ในเอกสาร Python

ดังนั้นวันนี้ผมอยากจะขอให้คุณสิ่งที่บางส่วนเคล็ดลับสำหรับการแก้จำกัด แหล่งที่มาของความท้าทายในการงูใหญ่?

โปรดระบุเพียง 1 เคล็ดลับต่อคำตอบ


เคล็ดลับดีๆที่นี่คืออะไร?

มีเกณฑ์สองสามข้อที่ฉันคิดว่าเคล็ดลับที่ดีควรมี:

  1. มันควรจะ (ค่อนข้าง) ไม่ชัดเจน

    คล้าย ๆ กับcode-golf tips น่าจะเป็นสิ่งที่คนที่เคยตีกอล์ฟใน python มาหน่อยแล้วอ่านหน้า tips จะไม่นึกถึงทันที ตัวอย่างเช่น "แทนที่a + bด้วยa+bเพื่อหลีกเลี่ยงการใช้ช่องว่าง" นักกอล์ฟทุกคนเห็นได้ชัดเนื่องจากเป็นวิธีที่ทำให้โค้ดของคุณสั้นลงอยู่แล้วจึงไม่ใช่เคล็ดลับที่ดี

  2. ไม่ควรเจาะจงเกินไป

    เนื่องจากข้อ จำกัด แหล่งที่มามีหลายประเภทคำตอบที่นี่จึงควรใช้ได้กับข้อ จำกัด หลายแหล่งหรือข้อ จำกัด แหล่งที่มาทั่วไปอย่างน้อยหนึ่งข้อ ตัวอย่างคำแนะนำของแบบฟอร์มHow to X โดยไม่ใช้อักขระ Yมักมีประโยชน์เนื่องจากอักขระที่ถูกแบนเป็นข้อ จำกัด ของแหล่งที่มาทั่วไป สิ่งที่เคล็ดลับของคุณช่วยในการทำควรเป็นเรื่องทั่วไปด้วย ตัวอย่างเคล็ดลับของแบบฟอร์มวิธีสร้างตัวเลขด้วยข้อ จำกัด Xมีประโยชน์เนื่องจากหลายโปรแกรมใช้ตัวเลขโดยไม่คำนึงถึงความท้าทาย เคล็ดลับของแบบฟอร์มวิธีใช้อัลกอริทึมของ Shor ด้วยข้อ จำกัด Xโดยพื้นฐานแล้วเป็นเพียงคำตอบสำหรับความท้าทายที่คุณเพิ่งคิดค้นและไม่เป็นประโยชน์กับผู้คนในการแก้ปัญหาอื่น

คำตอบ

24 LuisMendo Aug 18 2020 at 21:45

หลีกเลี่ยงตัวอักษร "ปกติ"

ตัวระบุถูกทำให้เป็นมาตรฐานโดยตัวแยกวิเคราะห์ Python 3 นี่ก็หมายความว่าเล่นหาง (Unicode) ตัวอักษรเช่นจะถูกตีความเป็นเทียบเท่า𝓪𝓫𝓬𝓓𝓔𝓕 ASCII ที่สอดคล้องกับของพวกเขา abcDEFดังนั้นรหัสต่อไปนี้ใช้งานได้ (ตามที่ใช้ประโยชน์ที่นี่ ):

𝓝=123
𝓹𝓻𝓲𝓷𝓽(𝓝)

เวอร์ชัน Python ที่ยืนยันพฤติกรรมนี้:

  • งาน: 3.4, 3.5, 3.6, 3.7, 3.8
  • ไม่ทำงาน: 2.7

ตัวอย่างข้อ จำกัด แหล่งที่มา:

  • ใช้ตัวอักษรไม่,abc···xyzABC···XYZ
21 AdHocGarfHunter Aug 18 2020 at 21:02

หลีกเลี่ยงตัวเลขที่มีบูลีน

เมื่อทำการคำนวณทางคณิตศาสตร์บนบูลีน Python จะถือว่าพวกมันเป็นตัวเลข 1 และ 0 ตัวอย่างเช่น

>>> True+False
1

คุณสามารถสร้างจำนวนบวกทั้งหมดได้เพียงแค่เพิ่มบูลีนเข้าด้วยกัน

คุณยังสามารถแทนที่บูลีนสำหรับค่าบูลีนได้เช่น[]>[]is Falseand [[]]>[]is Trueso

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

ในบางกรณี Booleans สามารถใช้แทนตัวเลขได้โดยไม่ต้องใช้เลขคณิตในการร่าย ตัวอย่างเช่นคุณสามารถจัดทำดัชนีเป็นรายการ / tuples / สตริงด้วยบูลีนได้ดังนั้น:

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

ตัวอย่างข้อ จำกัด แหล่งที่มา:

  • ไม่ใช้ตัวเลข ( 0123456789)

  • ไม่ใช้อักขระที่เป็นตัวเลขและตัวอักษร

  • ไม่ใช้ifเงื่อนไข

19 AdHocGarfHunter Aug 18 2020 at 21:12

หลีกเลี่ยง Parens ด้วยการจัดทำดัชนีรายการ

วงเล็บมีประโยชน์อย่างยิ่งสำหรับการสร้างลำดับความสำคัญของตัวดำเนินการที่ถูกต้องดังนั้นจึงเป็นเรื่องที่น่าเบื่อเมื่อถูกแบน อย่างไรก็ตามหาก[]ยังมีอยู่เราสามารถใช้แทนได้ เพียงแค่เปลี่ยน

(...)

ด้วย

[...][0]

สิ่งนี้จะสร้างรายการและทำดัชนีเพื่อให้ได้องค์ประกอบเดียว รายการทำให้ภายในได้รับการประเมินก่อนเพื่อแก้ปัญหาลำดับความสำคัญของคุณ

ตัวอย่างด้านบนใช้อักขระ[]0เพื่อทำสิ่งนี้ แต่มีอักขระที่สามอื่น ๆ ที่สามารถใช้ในกรณีนี้ได้หากจำเป็น

  • การใช้อักขระ[]>เขียน[...][[]>[]]
  • การใช้อักขระ[]<เขียน[...][[]<[]]
  • การใช้อักขระ[]=เขียน[...][[[]]==[]]

ตัวอย่างข้อ จำกัด แหล่งที่มา:

  • ไม่ต้องใช้วงเล็บ
16 WheatWizard Aug 18 2020 at 22:16

การเรียกใช้ฟังก์ชันโดยไม่มีวงเล็บ

เราสามารถหลีกเลี่ยงการใช้วงเล็บสำหรับลำดับความสำคัญของตัวดำเนินการได้โดยใช้การจัดทำดัชนีรายการแต่วงเล็บยังคงมีประโยชน์มากสำหรับการเรียกใช้ฟังก์ชัน

การจัดทำดัชนีรายการสามารถใช้ที่นี่เพื่อแก้ปัญหาได้ แต่มันซับซ้อนกว่ามากดังนั้นฉันจึงได้คำตอบของมันเอง

ในการเรียกใช้ฟังก์ชันเราเริ่มต้นด้วยการสร้างคลาสใหม่ซึ่งมีการกำหนดดัชนีให้เป็นฟังก์ชัน ดังนั้นถ้าเราต้องการเรียกprintสิ่งนี้อาจมีลักษณะดังนี้

class c:__class_getitem__=print

จากนั้นในการเรียกใช้ฟังก์ชันเราก็ทำดัชนีด้วยอาร์กิวเมนต์ที่เราต้องการ ตัวอย่างเช่นในการพิมพ์ที่"Hello World"เราทำ

c["Hello World"]

สิ่งนี้มีการมาถึงสั้น ๆ ที่โชคร้าย:

  • สามารถใช้เพื่อเรียกใช้ฟังก์ชันที่มีพารามิเตอร์เดียวเท่านั้น
  • มีอักขระไม่กี่ตัวที่ต้องใช้เพื่อดึงเคล็ดลับนี้ออก ( :=[]_acegilmst)
  • มันใช้อักขระจำนวนมากหากคุณกำลังทำโค้ดกอล์ฟ

แต่บางครั้งอาจเป็นทางเลือกเดียวของคุณ


ตัวอย่างข้อ จำกัด แหล่งที่มา:

  • ไม่ต้องใช้วงเล็บ

นี่คือตัวอย่างของการใช้งาน

14 null Aug 19 2020 at 07:59

ใช้<<และ|สร้างค่าคงที่โดยไม่ต้อง+

สนุกจริง: คุณจะได้รับอย่างต่อเนื่องบวกใด ๆ []<|เพียงแค่ใช้ วิธีที่จะไปคือการเปลี่ยนบูลีนทางซ้าย []<[[]]คือ 1 ดังนั้น[]<[[]]<<[]<[[]]ควรเลื่อนไปทางซ้าย 1 กับ 1 ซึ่งก็คือ 2

ได้ผลหรือไม่?

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

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

... เปล่าครับ

ลำดับความสำคัญผิด โชคดีที่เราสามารถแก้ไขปัญหานี้ได้ด้วย "Ad Hoc Garf Hunter Parenthesis (TM)":

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

อ๊าาา!

เพื่อให้ได้ตัวเลขอื่นที่ไม่ใช่กำลังสองคุณยังต้องการ+... หรือไม่ |หรือ[bitwise or][[]<[]]* จะทำเพื่อคุณ

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

เพื่อให้ได้ตัวเลขที่ติดลบโดยไม่ต้องคุณอาจต้องการที่จะใช้-~

* ส่วนนั้นอยู่ใน "Ad Hoc Garf Hunter Parenthesis (TM)" เพื่อระบุลำดับความสำคัญ

12 water_ghosts Aug 19 2020 at 06:45

เข้าถึงวิธีการและฟังก์ชันในตัวผ่าน __dict__

คลาสประกอบด้วย__dict__แอตทริบิวต์ซึ่งแมปชื่อเมธอดกับเมธอด __dict__ถ้าคุณไม่สามารถพิมพ์ชื่อวิธีการโดยตรงคุณสามารถรับพวกเขาจากนี้

ตัวอย่างเช่นสมมติว่าคุณต้องต่อท้ายรายการ แต่คุณไม่สามารถใช้อักขระp,n,+ได้เป็นต้นเนื่องจากappend()เป็นวิธีที่ 26 ในรายการ__dict__คุณสามารถเรียกใช้เมธอดดังนี้:

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

ลองออนไลน์!

นอกจากนี้ยังสามารถใช้__builtins__เพื่อเข้าถึงฟังก์ชันในตัว แม้ว่าจะมีคนห้ามอักขระxเพื่อบล็อกexecฟังก์ชันนี้คุณก็ยังสามารถเรียกexecสิ่งนี้ได้:

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

ลองออนไลน์!

วิธีนี้ใช้ได้ดีที่สุดใน Python เวอร์ชันล่าสุดที่รับประกันคำสั่งพจนานุกรม แต่อาจมีวิธีอื่นในการใช้สิ่งนี้ในเวอร์ชันเก่าเช่นการวนซ้ำ__dict__ด้วย regex หรือการจับคู่สตริงย่อย

12 Arnauld Aug 18 2020 at 21:59

ใช้ord()หรือสตริงไบนารีเพื่อหลีกเลี่ยงตัวเลข

จำนวนเต็มส่วนใหญ่ในช่วง[32..47]และ[58..126]สามารถหาได้อย่างง่ายดายจากรหัส ASCII ของอักขระเดี่ยวด้วย:

x=ord('A')

# or, if parentheses are not allowed:

y=b'A'[False]

ลองออนไลน์!

นอกจากนี้ยังสามารถสร้างจำนวนเต็มขนาดใหญ่ได้โดยใช้จากจุดยูนิโคด:

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

หากคุณสามารถใช้การกำหนดหรือคุณต้องการหลีกเลี่ยงวงเล็บคุณสามารถแกะค่าแทนได้ โปรดทราบว่าสิ่งนี้จะไม่ทำงานแบบอินไลน์แม้ว่าจะใช้ตัวดำเนินการวอลรัสก็ตาม

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

ลองออนไลน์!

ตัวอย่างข้อ จำกัด แหล่งที่มา:

  • ไม่ใช้ตัวเลข
  • ไม่ใช้ตัวเลข / ไม่มีวงเล็บ / ไม่มีวงเล็บ
9 Noname Aug 18 2020 at 21:31

ใช้--เพื่อหลีกเลี่ยง +

เช่นสิ่งที่ต้องทำa+b:

a--b

ตัวอย่างข้อ จำกัด แหล่งที่มา:

  • หลีกเลี่ยงตัว+ดำเนินการ
6 water_ghosts Aug 21 2020 at 12:19

ทางเลือกในการevalและexec

คุณจำเป็นต้องถือว่าสตริงเป็นรหัส แต่คุณไม่สามารถใช้evalหรือexec? มีอย่างน้อยสามวิธีอื่น ๆ ในการดำเนินการสตริง:

1) timeit.timeit

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

ลองออนไลน์!

timeitnumberเวลาทำงานและส่งกลับค่าเฉลี่ยของระยะเวลาที่ใช้ โดยค่าเริ่มต้นจะทำงาน 1 ล้านครั้งดังนั้นคุณแทบจะต้องการตั้งค่าnumber=1หรือเพิ่มข้อยกเว้นเพื่อแยกออก (เช่น"print('hello'); 0/0")

ขอบคุณEthan White ที่แสดงให้ฉันเห็นแนวทางนี้

2) os.system

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

ลองออนไลน์!

os.systemรันคำสั่งเชลล์โดยพลการและส่งคืนรหัสทางออก หากคุณต้องการพิมพ์บางอย่างคุณสามารถติดได้echoแต่คุณยังสามารถเรียกใช้รหัสโดยอำเภอใจได้ด้วยการเรียกpython3ตัวเอง

3) code.InteractiveInterpreter (). runcode

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

ลองออนไลน์!

codeได้รับการออกแบบมาโดยเฉพาะสำหรับลูปแบบ read-eval-print และแม้ว่ามันจะค่อนข้างเกะกะ แต่นี่ก็เป็นสิ่งที่ทรงพลังที่สุดในสาม timeitและos.systemแยกกระบวนการของพวกเขาออก แต่InteractiveInterpreterสามารถใช้ global state แทนของมันเอง:

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

ลองออนไลน์!

4 Noname Aug 18 2020 at 21:41

ใช้*เพื่อหลีกเลี่ยง/

x**-1เทียบเท่ากับ1/x. ดังนั้นจะทำอย่างไรที่คุณสามารถทำได้y/xx**-1*y

หากคุณต้องการกำจัดสิ่งนี้อย่างสิ้นหวัง-1คุณสามารถดูเคล็ดลับอื่น ๆ ของ Ad Hoc Garf Hunter

ตัวอย่างข้อ จำกัด แหล่งที่มา:

  • หลีกเลี่ยงการใช้/ตัวละคร
4 water_ghosts Aug 19 2020 at 06:19

เข้ารหัสอักขระที่ จำกัด และใช้ exec()

เช่นเดียวกับการแปลภาษามากที่สุด, Python สามารถเรียกใช้สตริงเป็นรหัสที่มีและeval มีข้อ จำกัด มากขึ้น แต่สามารถจัดการกับการนำเข้านิยามฟังก์ชันลูปข้อยกเว้น ฯลฯexecevalexec

เมื่อรวมกับเคล็ดลับอื่น ๆ เกี่ยวกับการเข้ารหัสอักขระจะช่วยให้คุณสามารถเขียนโค้ดได้ตามปกติ:

import sys

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

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

จากนั้นเลือกการเข้ารหัสและส่งผ่านเวอร์ชันที่เข้ารหัสไปที่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')

ลองออนไลน์!

4 water_ghosts Aug 19 2020 at 13:38

แทนที่ตัวดำเนินการด้วยวิธี dunder

ตัวดำเนินการ python ส่วนใหญ่เป็นน้ำตาลที่เป็นประโยคสำหรับการเรียกเมธอดเฉพาะ (มักเรียกว่า "magic method" หรือ "dunder method" โดย "dunder" จะย่อมาจาก "double underscore") ยกตัวอย่างเช่น+การโทร__add__(), ==สาย__eq__()และ<<โทรออก__lshift__()

หากตัวดำเนินการถูก จำกัด คุณสามารถเรียกใช้เมธอดเหล่านี้ได้โดยตรง:

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

ลองออนไลน์!

สำหรับการกำหนดคุณสามารถใช้__setitem__บนlocals()หรือglobals()พจนานุกรมหรือไม่ว่าตัวแปรที่มีอยู่แล้ว:

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

ลองออนไลน์!

โปรดทราบว่าคุณจะต้องเพิ่มวงเล็บรอบ ๆ ตัวเลขเพื่อหลีกเลี่ยงข้อผิดพลาดทางไวยากรณ์ 4.__eq__(4)จะไม่ทำงาน แต่(4).__eq__(4)จะ

3 Noodle9 Aug 18 2020 at 21:26

วิธีสร้างอักขระด้วยตัวเลขเครื่องหมายคำพูดและเครื่องหมายแบ็กสแลช

สตริงสามารถประกอบด้วยโดย\oooที่oooค่าฐานแปดของอักขระ

เช่น:

'\141'=='a'

นอกจากนี้คุณยังสามารถใช้ hex, ค่าใช้จ่ายของนั้นx(และa, b, c, d, eและ / หรือfหากพวกเขากำลังใช้):

'\x61'=='a'  

และ Unicode โดยเสียค่าใช้จ่ายของu(สองตัวก่อน Python 3) และอักขระฐานสิบหกหากใช้:

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

ใช้__import__("module")แทนimport module

เพื่อหลีกเลี่ยงช่องว่างในimport statementหรือสร้างชื่อโมดูลแบบไดนามิกเพื่ออิมพอร์ตเป็นสตริง (เช่น"RANDOM".lower()ถ้าคุณไม่สามารถใช้ตัวพิมพ์เล็กได้d) ไม่น่าจะมีประโยชน์เพราะคุณไม่ได้มักจะต้องห้องสมุดมาตรฐานและคุณยังจะต้องมีความสามารถในการใช้งาน_, i, m, p, o, r, t, และ()

แก้ไข: หรือตามที่ Ad Hoc Garf Hunter แนะนำคุณสามารถใช้import<tab>module(ด้วยอักขระแท็บตามตัวอักษร)!

2 OskarSkog Aug 21 2020 at 22:57

สิ่งนี้อาจไม่เกี่ยวข้องกับคำถาม แต่มันโง่มาก

นี่เป็นวิธีที่พกพาได้ง่ายกว่าของวิธีการที่ซับซ้อนของ water_ghost ในการเข้าถึงวิธีการในตัว
ดัชนีคือ 26 เฉพาะใน CPython 3 การปรับเปลี่ยนที่เล็กมากและเข้าใจง่ายมากช่วยให้ทำงานบน CPython 2.7, CPython 3, PyPy 2.7 และ PyPy 3 (ทดสอบบน 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)

ดัชนีที่ถูกต้อง (ในระบบของฉัน) คือ

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

และโดยบังเอิญโชคดี('C'+'2')%4 == 1, ('C'+'3')%4 == 2, และ('P'+'2') == 2 ('P'+'3') == 3ค่า 14 มีไว้เพื่อหลอกให้คุณคิดว่ามีรูปแบบ

2 null Aug 27 2020 at 18:30

__debug__ เป็นความจริง

ระเบิดตัวเองสวย ... ระเบิด ...

>>> __debug__
True