1 puan yazan GN⁺ 2024-08-23 | 1 yorum | WhatsApp'ta paylaş

Python'un Ön İşlemcisi

  • Python'da ön işlemci olmadığı iddiası doğru değil
  • Python'ın çok güçlü bir ön işlemcisi var

Python kaynak kodu kodlaması

  • PEP-0263 sayesinde kaynak kodu kodlaması tanımlanabilir
  • İlk iki satıra magic comment eklenerek kodlama ayarlanabilir
  • Örnekler: # coding=utf8, # -*- coding: utf8 -*-, # vim: set fileencoding=utf8 :

Yol yapılandırma dosyaları (.pth)

  • Python yorumlayıcısı -S seçeneği olmadan başlatıldığında site paketini otomatik olarak yükler
  • Modül arama yolunu genişletmek için site-packages klasörüne .pth dosyaları eklenebilir
  • .pth dosyasındaki import ile başlayan satırlar çalıştırılır
  • Bu sayede Python yorumlayıcısı başlatılırken keyfi kod çalıştırılabilir

Özel codec tanımı

  • Python yorumlayıcısının karşılamasını beklediği iki gereksinim vardır:
    • decode(data: bytes) -> tuple[str, int] fonksiyonu
    • artımlı decoder sınıfı
  • Gerçek decode işlemini yapmak için codecs.utf_8_decode kullanılır ve sonuç dizesi ön işlemciye aktarılır
  • İstisnaları yakalayıp yazdırmak ve sonra yeniden fırlatmak iyi bir yaklaşımdır

Artımlı decoder sağlama

  • Artımlı decoder, codecs.BufferedIncrementalDecoder sınıfından türetilerek uygulanabilir
  • Veriler tamponda toplanır ve son decode çağrısında tüm dosya ön işlemden geçirilir

Python'ı genişletmek

  • Python'ın standart kütüphanesini kullanarak Python'ı genişletmek nispeten kolaydır
  • Dosyanın token akışını değiştirmek için tokenize modülü, soyut sözdizim ağacını değiştirmek için ast modülü kullanılabilir
Tekli artırma ve azaltma
  • Python'da tekli artırma ve azaltma operatörleri yoktur
  • x++, x-- geçerli değildir
  • ++x, --x geçerlidir ama farklı anlam taşır
  • Tekli artırma ve azaltma ifadeleri Python ifadelerine dönüştürülebilir
Örnek
  • Girdi dosyası incdec.py:
    # coding: magic.incdec
    i = 6
    assert i-- == 6
    assert i == 5
    assert ++i == 6
    assert --i == 5
    assert i++ == 5
    assert i == 6
    assert (++i, 'i++') == (7, 'i++')
    print("PASSED")
    
  • Dönüştürülmüş dosya:
    i = 6
    assert ((i, i := i - 1)[0]) == 6
    assert i == 5
    assert ((i, i := i + 1)[1]) == 6
    assert ((i, i := i - 1)[1]) == 5
    assert ((i, i := i + 1)[0]) == 5
    assert i == 6
    assert (((i, i := i + 1)[1]), 'i++') == (7, 'i++')
    print("PASSED")
    

Süslü parantez kullanan Python (Bython)

  • Kapsam, Python'ın girintisi yerine süslü parantezlerle belirtilebilir
  • Token akışını değiştirmek için tokenize.generate_tokens kullanılabilir
Örnek
  • Girdi dosyası test.by:
    # coding: magic.braces
    def print_message(num_of_times) {
      for i in range(num_of_times) {
        print("braces ftw")
      }
      print({'x': 3})
    }
    x = {
      'foo': 42,
      'bar': 5
    }
    if __name__ == "__main__" {
      print_message(2)
      print({k: v for k, v in x.items()})
    }
    
  • Dönüştürülmüş dosya:
    def print_message(num_of_times):
      for i in range(num_of_times):
        print("braces ftw")
      print({'x': 3})
    x = {
      'foo': 42,
      'bar': 5
    }
    if __name__ == "__main__":
      print_message(2)
      print({k: v for k, v in x.items()})
    

Başka dilleri yorumlama

  • Python yorumlayıcısının başka dilleri yorumlaması sağlanabilir
  • Örneğin: C, C++, TOML
Örnek
  • C++ dosyası test.cpp:
    #define CODEC "coding:magic.cpp"
    #include <cstdio>
    int main() {
      puts("Hello World");
    }
    
  • Dönüştürülmüş dosya:
    import cppyy
    cppyy.cppdef(r"""
    #define CODEC "coding:magic.cpp"
    #include <cstdio>
    int main() {
      puts("Hello World");
    }
    """)
    from cppyy.gbl import main
    if __name__ == "__main__":
      main()
    

Veri doğrulama

  • TOML biçimindeki veriler JSON şeması kullanılarak doğrulanabilir
  • Gerçek doğrulama işlemi için jsonschema kullanılır
Örnek
  • Şema dosyası schema.json:
    {
      "type": "object",
      "properties": {
        "name": {"type": "string"},
        "age": {"type": "number"},
        "scores": {
          "type": "array",
          "items": {"type": "number"}
        },
        "address": {"$ref": "#/$defs/address"}
      },
      "required": ["name"],
      "$defs": {
        "address": {
          "type": "object",
          "properties": {
            "street": {"type": "string"},
            "postcode": {"type": "number"}
          },
          "required": ["street"]
        }
      }
    }
    
  • Geçerli veri dosyası data_valid.toml:
    # coding: magic.toml
    name = "John Doe"
    age = 42
    scores = [40, 20, 80, 90]
    [address]
    street = "Grove St. 4"
    postcode = 19201
    
  • Geçersiz veri dosyası data_invalid.toml:
    # coding: magic.toml
    name = "John Doe"
    age = 42
    scores = [40, "20", 80, 90]
    [address]
    street = "Grove St. 4"
    postcode = 19201
    

Sonuç

  • Özel codec'ler ve yol yapılandırma dosyaları kullanılarak Python yorumlayıcısının davranışı büyük ölçüde değiştirilebilir
  • Örnekler arasında pythonql, future-typing, future-fstrings, future-annotations yer alıyor
  • Kendi ön işlemcinizi kolayca denemek için magic_codec kullanılabilir

GN⁺ Özeti

  • Python'un ön işlemcisi kullanılarak çeşitli dil genişletmeleri ve veri doğrulama işlemleri yapılabilir
  • Özel codec'ler aracılığıyla Python yorumlayıcısının davranışının nasıl değiştirilebileceği açıklanıyor
  • Bu yazı, Python geliştiricileri için yararlı araçlar ve teknikler sunuyor
  • Benzer işlevlere sahip projeler arasında pythonql, future-typing ve diğerleri bulunuyor

1 yorum

 
GN⁺ 2024-08-23
Hacker News yorumu
  • from __future__ import braces sözdizimi hatası mesajı 2001'den beri cpython içinde hardcode edilmiş durumda

    • Jeremy Hylton tarafından yazılmış; kendisi şu anda Google'da yapay zeka arama kalitesinden sorumlu kıdemli mühendis olarak çalışıyor
    • 24 yıl içinde bir kişinin kariyerinin, belirli bir sözdizimi yasağını anmakla başlayıp özel sözdizimi gerektirmeyen bir arama sistemi üzerinde çalışmaya evrilmesi şaşırtıcı
  • import-hooks kullanarak yaratıcı bir işten çıkarılma yöntemi düşündüm, ama codec regex'in "μtf8" gibi bir şeyi kullanmayı engellemesi üzücü

    • import hooks, preprocessors ve sys.settrace kullanarak tüm fonksiyonları daha önce çağrılmış fonksiyonlarla monkey patch'lemek ve stdout ile stderr'yi her 17 dakikada bir değiştirmek gibi bir yöntem kullanmak gerekiyor
  • Python'ın preprocessor hook'larını dışa açmamasının bir nedeni var ve bence makul yetişkinlerin bundan kaçınması gerekir

    • Ama makul yetişkinlerle ilgilenmeden eğlencenin peşinden gitmek istiyorum
  • preprocessor'ler daha kullanışlı ve faydalı

    • ast modülünü kullanarak kodu yeniden yazıp exec ile çalıştırdıktan sonra exit() ekleyen bir hack yaptım
    • Tüm dict'ler sıralı hale gelmeden önce, ast yeniden yazım özelliğini faydalı olacak şekilde kullanıyordum
  • Python'ın esnekliğini seviyorum

    • Yaptığım en lanetli iş, string'leri yerinde dönüştürmekti; mmap'i suistimal ederek script'in kendisini dönüştürmesini sağlamıştım
    • Artık bir Lisp yorumlayıcısı yazmak istiyorum
  • pyxl ile ilgili en iyi kullanım örneği JSX'ten ilham alıyor

    • # coding: pyxl kullanarak HTML kodu yazabiliyorsunuz
  • Python 2'den 3'e geçişin daha iyi yönetilip yönetilemeyeceğini merak ediyorum

    • # coding: six.python2, Python2 kodunu Python3'te geçerli hale getirebilir ve # coding: six.python3, Python3 kodunu Python2'de çalıştırılabilir olacak şekilde uyarlayabilir
  • Bu fikri beğenmenize sevindim, yakında daha fazlası gelecek

  • Uzun zamandır ilk kez tamamen yeni bir fikir karşısında şaşkınlık hissettim

  • Python'da satır içi kod üretimi istiyorsanız, Ned Batchelder'in cog aracını kullanabilirsiniz

  • Bu coding hook stratejisiyle eklenen bağımlılıkların pip freeze veya uv tarafından tespit edilip edilmediğini merak ediyorum

    • Eğer edilmiyorsa, keyfini çıkarın. Kütüphaneyi yeniden yazmak daha kolay olabilir (böyle bir tuzak varsa başka tuzakların da olması muhtemeldir)