Python'da kısıtlı kaynak için ipuçları

Aug 18 2020

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:

  1. (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 + bile a+biyi 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.

  2. Ç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

24 LuisMendo Aug 18 2020 at 21:45

"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.
21 AdHocGarfHunter Aug 18 2020 at 21:02

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 Falseve [[]]>[]bir Trueo 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 ifkoşul kullanma

19 AdHocGarfHunter Aug 18 2020 at 21:12

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
16 WheatWizard Aug 18 2020 at 22:16

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 printbu şö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.

14 null Aug 19 2020 at 07:59

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.

12 water_ghosts Aug 19 2020 at 06:45

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 xengellemek 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 .

12 Arnauld Aug 18 2020 at 21:59

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
9 Noname Aug 18 2020 at 21:31

--Önlemek için kullanın +

Örneğin yapmak için a+b:

a--b

Örnek kaynak kısıtlaması:

  • +Operatörden kaçının
6 water_ghosts Aug 21 2020 at 12:19

Alternatifler evalveexec

Bir dizeyi kod olarak ele almanız gerekiyor, ancak evalveya 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 numbersü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=1bir 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.systemrastgele 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 python3kendisini 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!

codeokuma-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. timeitve os.systemsüreçlerini izole eder, ancak InteractiveInterpreterkü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!

4 Noname Aug 18 2020 at 21:41

*Önlemek için kullanın/

x**-1eşdeğerdir 1/x. Yani y/xyapabilirsin x**-1*y.

Şüphesiz ondan kurtulmak -1istiyorsanız, Ad Hoc Garf Hunter'ın diğer ipucuna göz atabilirsiniz.

Örnek kaynak kısıtlaması:

  • /Karakteri kullanmaktan kaçının
4 water_ghosts Aug 19 2020 at 06:19

Kısıtlanmış karakterleri kodlayın ve kullanın exec()

En yorumlanır dillerin gibi, Python ile kod olarak bir dize çalıştırabilir evalve exec. evaldaha sınırlıdır, ancak execiç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!

4 water_ghosts Aug 19 2020 at 13:38

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.

3 Noodle9 Aug 18 2020 at 21:26

Yalnızca sayılar, tırnaklar ve ters eğik çizgilerle karakterler nasıl oluşturulur

Dizeler , karakterin sekizlik değerinin \ooonerede oooolduğu ile oluşturulabilir .

Örneğin:

'\141'=='a'

Ayrıca, bir pahasına, onaltılık kullanabilirsiniz x(ve a, b, c, d, eve / veya fkullanılırlarsa):

'\x61'=='a'  

Ve a u(Python 3 öncesi iki) ve kullanıldıkları takdirde onaltılık karakterler pahasına unicode :

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

Yerine __import__("module")kullanınimport module

İçerisindeki boşluklardan kaçınmak için import statementveya 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)!

2 OskarSkog Aug 21 2020 at 22:57

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') == 2ve ('P'+'3') == 3. 14 değeri, bir kalıp olduğunu düşünmeniz için sizi kandırmak içindir.

2 null Aug 27 2020 at 18:30

__debug__ doğru

Oldukça kendi kendini ifade ...

>>> __debug__
True