Python'da kısıtlı kaynak için ipuçları
Kod golfü gibi , sınırlı kaynak da kişiyi Python dilinin tuhaflıklarından ve gizli özelliklerinden yararlanmaya iter. Kod golfü için tüm bu ipuçlarını toplayabileceğimiz bir yerimiz var , sınırlı kaynak için olanlar kulaktan kulağa iletiliyor veya python belgelerinin derinliklerinde gizli kalıyor.
Öyleyse bugün size Python'da kısıtlı kaynaklı zorlukları çözmek için bazı ipuçlarının neler olduğunu sormak istiyorum.
Lütfen yanıt başına yalnızca 1 ipucu ekleyin.
Burada iyi bir ipucu yapan nedir?
İyi bir ipucunun sahip olması gereken birkaç kriter var:
(Bir şekilde) aşikar olmamalıdır.
Kod golf ipuçlarına benzer şekilde , python'da biraz golf oynayan ve ipuçları sayfasını okuyan birinin hemen aklına gelmeyeceği bir şey olmalıdır. Örneğin "Değiştir
a + b
ilea+b
iyi bir ipucu böylece kod daha kısa ve değil yapmak için bir yol zaten olduğundan boşluk kullanmaktan kaçınmak için," herhangi bir golfçü için açıktır.Çok spesifik olmamalı.
Birçok farklı türde kaynak kısıtlaması olduğundan, buradaki yanıtlar en azından bir şekilde birden çok kaynak kısıtlamasına veya bir ortak kaynak kısıtlamasına uygulanabilir olmalıdır. Örneğin Y karakter (ler) i kullanmadan X Nasıl Yapılır formunun ipuçları genellikle kullanışlıdır çünkü yasaklanmış karakterler yaygın bir kaynak kısıtlamasıdır. Bahşişinizin yardımcı olduğu şey de biraz genel olmalıdır. Örneğin , X kısıtlamasıyla sayılar nasıl oluşturulur formunun ipuçları , zorluklardan bağımsız olarak birçok program sayıları kullandığından kullanışlıdır. Formun ipuçları Shor'un algoritmasının X kısıtlamasıyla nasıl uygulanacağı temelde sadece sizin icat ettiğiniz bir zorluğun yanıtlarıdır ve diğer zorlukları çözen insanlara pek yardımcı olmaz.
Yanıtlar
"Normal" harflerden kaçının
Tanımlayıcılar Python 3 ayrıştırıcı tarafından normalleştirilir . Bu, el yazısı (Unicode) gibi harflerin 𝓪𝓫𝓬𝓓𝓔𝓕
ASCII uyumlu eşdeğerleri olarak yorumlandığı anlamına gelir abcDEF
. Dolayısıyla aşağıdaki kod çalışır ( burada kullanıldığı gibi ):
𝓝=123
𝓹𝓻𝓲𝓷𝓽(𝓝)
Bu davranışın onaylandığı Python sürümleri:
- İşler: 3.4, 3.5, 3.6, 3.7, 3.8
- Çalışmıyor: 2.7
Örnek kaynak kısıtlaması:
- Hiçbir karakter kullanın
abc···xyz
,ABC···XYZ
.
Boole'lu Sayılardan Kaçının
Boole'lerde aritmetik işlemler gerçekleştirirken, Python onlara 1 ve 0 sayıları gibi davranır. Örneğin,
>>> True+False
1
Tüm pozitif sayıları yalnızca boole'leri birbirine ekleyerek yapabilirsiniz.
Örnek için de boolean değerler için boole yerini alabilir []>[]
olduğunu False
ve [[]]>[]
bir True
o kadar
>>> ([]>[])+([[]]>[])
1
Bazı durumlarda, Boole'lar, onu çevirmek için aritmetik kullanmak zorunda kalmadan bir sayı yerine kullanılabilir. Örneğin, Boolean ile listeler / tuples / dizgeleri indeksleyebilirsiniz, böylece:
>>> ['a','b'][True]
'b'
Örnek kaynak kısıtlamaları:
Rakam kullanmayın (
0123456789
)Alfasayısal karakter kullanmayın
Hiçbir
if
koşul kullanma
Liste indeksleme ile Parens'ten kaçının
Parantezler, doğru operatör önceliğini oluşturmak için çok kullanışlıdır, bu nedenle yasaklandıklarında bir serseri olur. Ancak []
hala mevcutsa, bunun yerine bunları kullanabiliriz. Basitçe değiştirin
(...)
ile
[...][0]
Bu, bir liste oluşturur ve tek öğesini elde etmek için dizine ekler. Liste, öncelik sorununuzu çözerek önce içerinin değerlendirilmesine neden olur.
Yukarıdaki örnek []0
, bunu yapmak için karakterleri kullanır, ancak gerekirse bu durumda kullanılabilecek başka üçüncü karakterler de vardır.
- Karakter
[]>
yazmak[...][[]>[]]
- Karakter
[]<
yazmak[...][[]<[]]
- Karakter
[]=
yazmak[...][[[]]==[]]
Örnek kaynak kısıtlaması:
- Parantez kullanmayın
Parantezsiz fonksiyon çağrıları
Liste indekslemeyi kullanarak operatör önceliği için parantez kullanmaktan kaçınabiliriz , ancak parantezler hala fonksiyonları çağırmak için çok kullanışlıdır.
Liste indeksleme burada da sorunu çözmek için kullanılabilir, ancak çok daha karmaşıktır, bu yüzden kendi cevabını yaptım.
Bir fonksiyonu çağırmak için, indekslemesi fonksiyon olarak tanımlanmış yeni bir sınıf oluşturarak başlarız. Yani aramak istersek print
bu şöyle görünebilir
class c:__class_getitem__=print
Daha sonra işlevi çağırmak için, onu istediğimiz bağımsız değişkenle indeksleriz. Örneğin yazdırmak "Hello World"
için yapıyoruz
c["Hello World"]
Bunun birkaç talihsiz eksikliği var:
- Yalnızca bir parametre ile işlevleri çağırmak için kullanılabilir.
- Bu numarayı başarmak için gereken epeyce karakter var. (
:=[]_acegilmst
) - Kod golfü yapıyorsanız çok fazla karakter kullanır
Ancak bazen tek seçeneğiniz bu olabilir.
Örnek kaynak kısıtlaması:
- Parantez kullanmayın
İşte kullanılmasının bir örneği.
Olmadan sabit üretmek için <<
ve kullanın|
+
Eğlenceli gerçek: Herhangi bir pozitif sabiti yalnızca kullanarak elde edebilirsiniz []<|
. Gitmenin yolu, bir booleanı sola kaydırmaktır. []<[[]]
1, yani []<[[]]<<[]<[[]]
1 ile sola kaydırmalı, yani 2.
Çalışıyor mu?
>>> []<[[]]<<[]<[[]]
Traceback (most recent call last):
File "<pyshell#29>", line 1, in <module>
[]<[[]]<<[]<[[]]
TypeError: unsupported operand type(s) for <<: 'list' and 'list'
...Hayır.
Öncelik yanlış. Neyse ki, bunu "Ad Hoc Garf Hunter Parenthesis (TM)" ile çözebiliriz:
>>> [[]<[[]]][[]<[]]<<[[]<[[]]][[]<[]]
2
Aha!
İkinin gücü dışındaki sayıları elde etmek için, hala ihtiyacınız var +
... ya da yok. |
veya [bitwise or][[]<[]]
* bunu sizin için yapacak.
>>> [[]<[[]]][[]<[]]<<[[]<[[]]][[]<[]]<<[[]<[[]]][[]<[]]<<[[]<[[]]][[]<[]]|[[]<[[]]][[]<[]]<<[[]<[[]]][[]<[]]
10
Olmadan negatif sayılar elde -
etmek için kullanmak isteyebilirsiniz ~
.
* Bu bölüm, önceliği belirtmek için "Ad Hoc Garf Hunter Parantezi (TM)" içine alınmıştır.
Yöntemlere ve yerleşik işlevlere erişim __dict__
Sınıflar __dict__
, yöntem adlarını yöntemlerin kendisiyle eşleyen bir öznitelik içerir . Doğrudan bir yöntem adı yazamazsanız, bunları buradan alabilirsiniz __dict__
.
Örneğin, bir listeye bir şey eklemeniz gerektiğini varsayalım, ancak karakterleri kullanamazsınız p,n,+
, vb append()
. Bir listenin 26. yöntemi olduğundan, yöntemi __dict__
şu şekilde çağırabilirsiniz:
a = [1,2,3]
list(a.__class__.__dict__.values())[26](a, 4)
print(a) # prints [1,2,3,4]
Çevrimiçi deneyin!
Bu, __builtins__
yerleşik işlevlere erişmek için ile de kullanılabilir . Birisi işlevi x
engellemek için karakteri yasaklasa bile exec
, yine de şu şekilde çağrı yapabilirsiniz exec
:
list(__builtins__.__dict__.values())[20]("print('Hello, World!')")
Çevrimiçi deneyin!
Bu, en iyi Python'un sözlük sırasını garanti eden daha yeni sürümlerinde işe yarar, ancak bunu eski sürümlerde kullanmanın başka yolları da vardır, örneğin __dict__
bir normal ifade veya alt dize eşleşmesi ile yineleme gibi .
ord()
Basamaklardan kaçınmak için veya ikili dizeler kullanın
Aralıklardaki çoğu tamsayı [32..47]
ve [58..126]
tek bir karakterin ASCII kodundan aşağıdakilerle kolayca elde edilebilir:
x=ord('A')
# or, if parentheses are not allowed:
y=b'A'[False]
Çevrimiçi deneyin!
Unicode noktaları kullanılarak daha büyük tamsayılar da üretilebilir:
>>>print (ord("±"))
177
>>> print (ord("π"))
960
Bir atama kullanabiliyorsanız veya köşeli parantezlerden kaçınmanız gerekiyorsa, bunun yerine değerleri paketinden çıkarabilirsiniz. Mors operatöründe bile bunun satır içi çalışmayacağını unutmayın.
x,*_=b'A'
y,_=b'A_'
Çevrimiçi deneyin!
Örnek kaynak kısıtlamaları:
- Rakam kullanmayın
- Rakam / parantez yok / köşeli parantez kullanmayın
--
Önlemek için kullanın +
Örneğin yapmak için a+b
:
a--b
Örnek kaynak kısıtlaması:
+
Operatörden kaçının
Alternatifler eval
veexec
Bir dizeyi kod olarak ele almanız gerekiyor, ancak eval
veya kullanamıyorsunuz exec
? Bir dizeyi yürütmenin en az üç başka yolu vardır:
1) timeit.timeit
import timeit
_=timeit.timeit("print('Hello!')", number=1)
Çevrimiçi deneyin!
timeit
çalışma number
süreleri ve ne kadar sürdüğünün ortalamasını döndürür. Varsayılan olarak 1 milyon kez çalışır, bu nedenle neredeyse kesin olarak number=1
bir istisna oluşturmak veya çıkarmak isteyeceksiniz (örneğin "print('hello'); 0/0"
).
Bana bu yaklaşımı gösterdiği için Ethan White'a teşekkürler .
2) os.system
import os
c='echo "import math;print(math.pi)" | python3'
_=os.system(c) # Prints 3.141592653589793
Çevrimiçi deneyin!
os.system
rastgele bir kabuk komutu çalıştırır ve çıkış kodunu döndürür. Bir şeyi yazdırmanız gerekiyorsa, bağlı kalabilirsiniz echo
, ancak python3
kendisini arayarak da isteğe bağlı kod çalıştırabilirsiniz .
3) code.InteractiveInterpreter (). Runcode
from code import InteractiveInterpreter as I
i = I()
i.runcode("print('Hello!')")
Çevrimiçi deneyin!
code
okuma-değerlendirme-yazdırma döngüleri için özel olarak tasarlanmıştır ve biraz hantal olsa da, bu üçünün en güçlüsüdür. timeit
ve os.system
süreçlerini izole eder, ancak InteractiveInterpreter
küresel durumu kendi yerine kullanabilir:
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
Çevrimiçi deneyin!
*
Önlemek için kullanın/
x**-1
eşdeğerdir 1/x
. Yani y/x
yapabilirsin x**-1*y
.
Şüphesiz ondan kurtulmak -1
istiyorsanız, Ad Hoc Garf Hunter'ın diğer ipucuna göz atabilirsiniz.
Örnek kaynak kısıtlaması:
/
Karakteri kullanmaktan kaçının
Kısıtlanmış karakterleri kodlayın ve kullanın exec()
En yorumlanır dillerin gibi, Python ile kod olarak bir dize çalıştırabilir eval
ve exec
. eval
daha sınırlıdır, ancak exec
içe aktarmaları, işlev tanımlarını, döngüleri, istisnaları vb. işleyebilir.
Karakterleri kodlamayla ilgili diğer bazı ipuçlarıyla birleştirildiğinde, bu, kodunuzu normal şekilde yazmanıza olanak tanır:
import sys
def f(i):
return 1 if i==1 else i*f(i-1)
i=int(sys.argv[1])
print(f(i))
Ardından bir kodlama seçin ve kodlanmış sürümü şuna iletin 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')
Çevrimiçi deneyin!
Operatörleri dunder yöntemleriyle değiştirin
Çoğu python operatörü, belirli yöntem çağrıları için sözdizimsel şekerdir (genellikle "sihirli yöntemler" veya "dunder yöntemleri", "dunder", "çift alt çizgi" nin kısaltmasıdır). Örneğin +
aramalar __add__()
, ==
aramalar __eq__()
ve <<
aramalar__lshift__()
Operatörler kısıtlanmışsa, bu yöntemleri doğrudan çağırabilirsiniz:
a = 1
print(a.__add__(1).__eq__(2)) # True
Çevrimiçi deneyin!
Atama için kullanabilirsiniz __setitem__
üzerinde locals()
veya globals()
değişken zaten var olsun veya olmasın, sözlüklerde:
a = 1
locals().__setitem__('a',2)
locals().__setitem__('b',2)
print(a.__add__(b).__eq__(4)) # True
Çevrimiçi deneyin!
Sözdizimi hatasını önlemek için sayıların etrafına parantez eklemeniz gerekeceğini unutmayın. 4.__eq__(4)
işe yaramayacak ama (4).__eq__(4)
çalışacak.
Yalnızca sayılar, tırnaklar ve ters eğik çizgilerle karakterler nasıl oluşturulur
Dizeler , karakterin sekizlik değerinin \ooo
nerede ooo
olduğu ile oluşturulabilir .
Örneğin:
'\141'=='a'
Ayrıca, bir pahasına, onaltılık kullanabilirsiniz x
(ve a
, b
, c
, d
, e
ve / veya f
kullanılırlarsa):
'\x61'=='a'
Ve a u
(Python 3 öncesi iki) ve kullanıldıkları takdirde onaltılık karakterler pahasına unicode :
'\u2713'=='✓'
Yerine __import__("module")
kullanınimport module
İçerisindeki boşluklardan kaçınmak için import statement
veya bir dizge olarak içe aktarılacak modülün adını dinamik olarak oluşturmak için (örneğin "RANDOM".lower()
, küçük harf kullanamıyorsanız d
). Değil olasılıkla sık sık standart kütüphane gerekir ve yok kullanışlı çünkü hala kullanımda edebilmek gerektiğini _
, i
, m
, p
, o
, r
, t
, (
, ve )
.
Düzenleme: veya Ad Hoc Garf Hunter'ın önerdiği gibi kullanabilirsiniz import<tab>module
(gerçek bir sekme karakteriyle)!
Bu muhtemelen soruyla alakalı değil ama çok saçma.
Bu, (belki) water_ghost'un yerleşik yöntemlere erişmenin daha taşınabilir bir yoludur.
Dizin yalnızca CPython 3'te 26'dır. Bu çok küçük ve anlaşılması son derece kolay değişiklik, CPython 2.7, CPython 3, PyPy 2.7 ve PyPy 3'te çalışmasına izin verir (Debian 10 amd64'te test edilmiştir)
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)
Doğru endeksler (benim sistemimde)
CPython 2.7 13
CPython 3 26
PyPy 2.7 26
PyPy 3 25
Ve şanslı bir tesadüf ('C'+'2')%4 == 1
, ('C'+'3')%4 == 2
, ('P'+'2') == 2
ve ('P'+'3') == 3
. 14 değeri, bir kalıp olduğunu düşünmeniz için sizi kandırmak içindir.
__debug__
doğru
Oldukça kendi kendini ifade ...
>>> __debug__
True