Python의 제한된 소스에 대한 팁

Aug 18 2020

code-golf 와 마찬가지로 , 제한된 소스 는 Python 언어의 기이함과 숨겨진 기능을 이용하도록 푸시합니다. 우리는 이미 code-golf에 대한 모든 을 수집 할 수 있는 장소 를 가지고 있으며 , 제한된 소스에 대한 은 입소문으로 전송되거나 파이썬 문서 깊숙이 숨겨져 있습니다.

그래서 오늘 저는 파이썬에서 제한된 소스 문제 를 해결하기위한 몇 가지 이 무엇인지 묻고 싶습니다 .

답변 당 1 개의 팁만 포함하십시오.


여기서 좋은 팁은 무엇입니까?

좋은 팁이 가져야한다고 생각하는 몇 가지 기준이 있습니다.

  1. (다소) 명확하지 않아야합니다.

    코드 골프 유사하게 파이썬 으로 골프 를 치고 팁 페이지를 읽은 사람은 즉시 생각하지 않을 것입니다. 예를 들어 "교체 a + ba+b그것은 좋은 팁 따라서 코드가 짧고되지 만들 수있는 방법이 이미 있기 때문에 공간을 사용하지 않는"어떤 골퍼에 분명하다.

  2. 너무 구체적이어서는 안됩니다.

    다양한 유형의 소스 제한이 있기 때문에 여기에 대한 답변은 적어도 여러 소스 제한 또는 하나의 공통 소스 제한에 어느 정도 적용 가능해야합니다. 예를 들어 문자 Y를 사용하지 않고 How to X 형식의 팁은 금지 된 문자가 일반적인 소스 제한이기 때문에 일반적으로 유용합니다. 팁이 도움이되는 것도 다소 일반적이어야합니다. 예를 들어 X 제한 으로 숫자를 만드는 방법 양식의 팁은 많은 프로그램이 도전과 관계없이 숫자를 사용하기 때문에 유용합니다. X 제한을 사용하여 Shor 알고리즘을 구현하는 방법 양식의 팁 은 기본적으로 방금 발명 한 문제에 대한 답변 일 뿐이며 다른 문제를 해결하는 사람들에게는 그다지 도움이되지 않습니다.

답변

24 LuisMendo Aug 18 2020 at 21:45

"일반적인"문자를 피하십시오

식별자는 Python 3 파서에 의해 정규화 됩니다. 이는와 같은 필기체 (유니 코드) 문자 𝓪𝓫𝓬𝓓𝓔𝓕가 ASCII 호환 문자로 해석 됨을 의미합니다 abcDEF. 따라서 다음 코드가 작동합니다 ( 여기 에서 악용 됨 ).

𝓝=123
𝓹𝓻𝓲𝓷𝓽(𝓝)

이 동작이 확인 된 Python 버전 :

  • 작동 : 3.4, 3.5, 3.6, 3.7, 3.8
  • 작동하지 않음 : 2.7

소스 제한의 예 :

  • 어떤 문자를 사용하지 abc···xyz, ABC···XYZ.
21 AdHocGarfHunter Aug 18 2020 at 21:02

부울로 숫자 피하기

부울에 대해 산술 연산을 수행 할 때 Python은이를 숫자 1과 0 인 것처럼 처리합니다. 예를 들어

>>> True+False
1

서로에 부울을 추가하여 모든 양수를 만들 수 있습니다.

부울 값을 부울 값으로 대체 할 수도 있습니다. 예를 들어 []>[]is Falseand [[]]>[]is Trueso

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

어떤 경우에는 부울을 캐스트하기 위해 산술을 사용할 필요없이 숫자 대신 사용할 수도 있습니다. 예를 들어 부울을 사용하여 목록 / 튜플 / 문자열로 색인을 생성 할 수 있습니다.

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

소스 제한의 예 :

  • 숫자 없음 ( 0123456789)

  • 영숫자를 사용하지 마십시오.

  • if조건 없음

19 AdHocGarfHunter Aug 18 2020 at 21:12

목록 인덱싱으로 괄호 방지

괄호는 올바른 연산자 우선 순위를 만드는 데 매우 유용하므로 금지 될 때 당황합니다. 그러나 []여전히 사용 가능한 경우 대신 사용할 수 있습니다. 간단히 교체

(...)

[...][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로 왼쪽으로 이동해야합니다. 이는 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

아하!

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__정규식 또는 하위 문자열 일치로 반복하는 것과 같이 이전 버전에서 이것을 사용하는 다른 방법이있을 수 있습니다 .

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시간을 실행 하고 소요 된 평균 시간을 반환합니다. 기본적으로 100 만 번 실행되므로 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읽기-평가-인쇄 루프를 위해 특별히 설계되었으며 약간 어색하지만 이것은 세 가지 중에서 가장 강력합니다. timeit그리고 os.system자신의 프로세스를 분리하지만,이 InteractiveInterpreter대신에 자신의 전역 상태를 사용할 수 있습니다 :

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/x할 수 있습니다 x**-1*y.

를 필사적으로 제거하고 싶다면 -1Ad Hoc Garf Hunter의 다른 팁을 확인할 수 있습니다.

소스 제한의 예 :

  • /캐릭터 사용을 피하십시오
4 water_ghosts Aug 19 2020 at 06:19

제한된 문자 인코딩 및 사용 exec()

대부분의 해석 언어와 마찬가지로 파이썬과 코드로 문자열을 실행할 수 있습니다 evalexec. eval더 제한적이지만 exec가져 오기, 함수 정의, 루프, 예외 등을 처리 할 수 ​​있습니다.

문자 인코딩에 대한 다른 팁과 함께 사용하면 코드를 정상적으로 작성할 수 있습니다.

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 메서드로 대체

대부분의 파이썬 연산자는 특정 메서드 호출에 대한 구문 설탕입니다 (종종 "매직 메서드"또는 "dunder 메서드"라고하며 "dunder"는 "double underscore"의 약자). 예 : +call __add__(), ==calls __eq__()<<calls__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

숫자, 따옴표 및 백 슬래시만으로 문자를 만드는 방법

문자열은 문자의 8 진수 값인 \ooo곳 으로 구성 될 수 있습니다 ooo.

예 :

'\141'=='a'

당신은 또한의 비용으로, 진수를 사용할 수있다 x(그리고 a, b, c, d, e및 / 또는 f그들이 사용하는 경우) :

'\x61'=='a'  

그리고 u사용되는 경우 (Python 3 이전 2 개) 및 16 진수 문자를 희생하여 유니 코드합니다 .

'\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의 복잡한 방법의 더 이식 가능한 방법입니다.
인덱스는 CPython 3에서만 26 개입니다. 매우 작고 이해하기 쉬운 수정으로 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__ 사실이다

꽤 자기 배제 ... expl ...

>>> __debug__
True