Ana içeriğe geç

Python Tiplerine Giriş

🌐 Translation by AI and humans

This translation was made by AI guided by humans. 🤝

It could have mistakes of misunderstanding the original meaning, or looking unnatural, etc. 🤖

You can improve this translation by helping us guide the AI LLM better.

English version

Python, isteğe bağlı "type hints" (diğer adıyla "type annotations") desteğine sahiptir.

Bu "type hints" veya annotations, bir değişkenin type'ını bildirmeye yarayan özel bir sözdizimidir.

Değişkenleriniz için type bildirerek, editörler ve araçlar size daha iyi destek sağlayabilir.

Bu, Python type hints hakkında sadece hızlı bir eğitim / bilgi tazeleme dokümanıdır. FastAPI ile kullanmak için gereken minimum bilgiyi kapsar... ki aslında bu çok azdır.

FastAPI tamamen bu type hints üzerine kuruludur; bunlar ona birçok avantaj ve fayda sağlar.

Ancak hiç FastAPI kullanmasanız bile, bunlar hakkında biraz öğrenmeniz size fayda sağlayacaktır.

Not

Eğer bir Python uzmanıysanız ve type hints hakkında her şeyi zaten biliyorsanız, sonraki bölüme geçin.

Motivasyon

Basit bir örnekle başlayalım:

def get_full_name(first_name, last_name):
    full_name = first_name.title() + " " + last_name.title()
    return full_name


print(get_full_name("john", "doe"))

Bu programı çalıştırınca şu çıktıyı alırsınız:

John Doe

Fonksiyon şunları yapar:

  • first_name ve last_name değerlerini alır.
  • title() ile her birinin ilk harfini büyük harfe çevirir.
  • Ortada bir boşluk olacak şekilde Concatenates eder.
def get_full_name(first_name, last_name):
    full_name = first_name.title() + " " + last_name.title()
    return full_name


print(get_full_name("john", "doe"))

Düzenleyelim

Bu çok basit bir program.

Ama şimdi bunu sıfırdan yazdığınızı hayal edin.

Bir noktada fonksiyon tanımını yazmaya başlamış olacaktınız, parametreler hazır...

Ama sonra "ilk harfi büyük harfe çeviren method"u çağırmanız gerekiyor.

upper mıydı? uppercase miydi? first_uppercase? capitalize?

Sonra eski programcı dostuyla denersiniz: editör autocomplete.

Fonksiyonun ilk parametresi olan first_name'i yazarsınız, sonra bir nokta (.) ve ardından autocomplete'i tetiklemek için Ctrl+Space'e basarsınız.

Ama ne yazık ki, işe yarar bir şey göremezsiniz:

Tipleri ekleyelim

Önceki sürümden tek bir satırı değiştirelim.

Fonksiyonun parametreleri olan şu parçayı:

    first_name, last_name

şuna çevireceğiz:

    first_name: str, last_name: str

Bu kadar.

Bunlar "type hints":

def get_full_name(first_name: str, last_name: str):
    full_name = first_name.title() + " " + last_name.title()
    return full_name


print(get_full_name("john", "doe"))

Bu, aşağıdaki gibi default değerler bildirmekle aynı şey değildir:

    first_name="john", last_name="doe"

Bu farklı bir şey.

Eşittir (=) değil, iki nokta (:) kullanıyoruz.

Ve type hints eklemek, normalde onlarsız ne oluyorsa onu değiştirmez.

Ama şimdi, type hints ile o fonksiyonu oluşturmanın ortasında olduğunuzu tekrar hayal edin.

Aynı noktada, Ctrl+Space ile autocomplete'i tetiklemeye çalışırsınız ve şunu görürsünüz:

Bununla birlikte, seçenekleri görerek kaydırabilirsiniz; ta ki "tanıdık gelen" seçeneği bulana kadar:

Daha fazla motivasyon

Şu fonksiyona bakın, zaten type hints içeriyor:

def get_name_with_age(name: str, age: int):
    name_with_age = name + " is this old: " + age
    return name_with_age

Editör değişkenlerin tiplerini bildiği için, sadece completion değil, aynı zamanda hata kontrolleri de alırsınız:

Artık bunu düzeltmeniz gerektiğini, age'i str(age) ile string'e çevirmeniz gerektiğini biliyorsunuz:

def get_name_with_age(name: str, age: int):
    name_with_age = name + " is this old: " + str(age)
    return name_with_age

Tipleri bildirmek

Type hints bildirmek için ana yeri az önce gördünüz: fonksiyon parametreleri.

Bu, FastAPI ile kullanırken de onları en çok kullanacağınız yerdir.

Basit tipler

Sadece str değil, tüm standart Python tiplerini bildirebilirsiniz.

Örneğin şunları kullanabilirsiniz:

  • int
  • float
  • bool
  • bytes
def get_items(item_a: str, item_b: int, item_c: float, item_d: bool, item_e: bytes):
    return item_a, item_b, item_c, item_d, item_e

Tip parametreleri ile Generic tipler

dict, list, set ve tuple gibi, başka değerler içerebilen bazı veri yapıları vardır. Ve iç değerlerin kendi tipi de olabilir.

İç tipleri olan bu tiplere "generic" tipler denir. Ve bunları, iç tipleriyle birlikte bildirmek mümkündür.

Bu tipleri ve iç tipleri bildirmek için standart Python modülü typing'i kullanabilirsiniz. Bu modül, özellikle bu type hints desteği için vardır.

Python'un daha yeni sürümleri

typing kullanan sözdizimi, Python 3.6'dan en yeni sürümlere kadar (Python 3.9, Python 3.10, vb. dahil) tüm sürümlerle uyumludur.

Python geliştikçe, daha yeni sürümler bu type annotations için daha iyi destekle gelir ve çoğu durumda type annotations bildirmek için typing modülünü import edip kullanmanız bile gerekmez.

Projeniz için daha yeni bir Python sürümü seçebiliyorsanız, bu ek sadelikten yararlanabilirsiniz.

Tüm dokümanlarda her Python sürümüyle uyumlu örnekler vardır (fark olduğunda).

Örneğin "Python 3.6+", Python 3.6 veya üstüyle (3.7, 3.8, 3.9, 3.10, vb. dahil) uyumludur. "Python 3.9+" ise Python 3.9 veya üstüyle (3.10 vb. dahil) uyumludur.

Eğer Python'un en güncel sürümlerini kullanabiliyorsanız, en güncel sürüme ait örnekleri kullanın; bunlar en iyi ve en basit sözdizimine sahip olur, örneğin "Python 3.10+".

List

Örneğin, str'lerden oluşan bir list olan bir değişken tanımlayalım.

Değişkeni, aynı iki nokta (:) sözdizimiyle bildirin.

Type olarak list yazın.

list, bazı iç tipleri barındıran bir tip olduğundan, bunları köşeli parantez içine yazarsınız:

def process_items(items: list[str]):
    for item in items:
        print(item)

Bilgi

Köşeli parantez içindeki bu iç tiplere "type parameters" denir.

Bu durumda str, list'e verilen type parameter'dır.

Bu şu demektir: "items değişkeni bir list ve bu listedeki her bir öğe str".

Bunu yaparak, editörünüz listeden öğeleri işlerken bile destek sağlayabilir:

Tipler olmadan, bunu başarmak neredeyse imkansızdır.

item değişkeninin, items listesindeki elemanlardan biri olduğuna dikkat edin.

Ve yine de editör bunun bir str olduğunu bilir ve buna göre destek sağlar.

Tuple ve Set

tuple'ları ve set'leri bildirmek için de aynısını yaparsınız:

def process_items(items_t: tuple[int, int, str], items_s: set[bytes]):
    return items_t, items_s

Bu şu anlama gelir:

  • items_t değişkeni 3 öğeli bir tuple'dır: bir int, bir başka int ve bir str.
  • items_s değişkeni bir set'tir ve her bir öğesi bytes tipindedir.

Dict

Bir dict tanımlamak için, virgülle ayrılmış 2 type parameter verirsiniz.

İlk type parameter, dict'in key'leri içindir.

İkinci type parameter, dict'in value'ları içindir:

def process_items(prices: dict[str, float]):
    for item_name, item_price in prices.items():
        print(item_name)
        print(item_price)

Bu şu anlama gelir:

  • prices değişkeni bir dict'tir:
    • Bu dict'in key'leri str tipindedir (örneğin her bir öğenin adı).
    • Bu dict'in value'ları float tipindedir (örneğin her bir öğenin fiyatı).

Union

Bir değişkenin birkaç tipten herhangi biri olabileceğini bildirebilirsiniz; örneğin bir int veya bir str.

Python 3.6 ve üzeri sürümlerde (Python 3.10 dahil), typing içinden Union tipini kullanabilir ve köşeli parantez içine kabul edilecek olası tipleri yazabilirsiniz.

Python 3.10'da ayrıca, olası tipleri vertical bar (|) ile ayırabildiğiniz yeni bir sözdizimi de vardır.

def process_item(item: int | str):
    print(item)
from typing import Union


def process_item(item: Union[int, str]):
    print(item)

Her iki durumda da bu, item'ın int veya str olabileceği anlamına gelir.

Muhtemelen None

Bir değerin str gibi bir tipi olabileceğini ama aynı zamanda None da olabileceğini bildirebilirsiniz.

Python 3.6 ve üzeri sürümlerde (Python 3.10 dahil), typing modülünden Optional import edip kullanarak bunu bildirebilirsiniz.

from typing import Optional


def say_hi(name: Optional[str] = None):
    if name is not None:
        print(f"Hey {name}!")
    else:
        print("Hello World")

Sadece str yerine Optional[str] kullanmak, aslında değer None olabilecekken her zaman str olduğunu varsaydığınız hataları editörün yakalamanıza yardımcı olmasını sağlar.

Optional[Something], aslında Union[Something, None] için bir kısayoldur; eşdeğerdirler.

Bu aynı zamanda Python 3.10'da Something | None kullanabileceğiniz anlamına gelir:

def say_hi(name: str | None = None):
    if name is not None:
        print(f"Hey {name}!")
    else:
        print("Hello World")
from typing import Optional


def say_hi(name: Optional[str] = None):
    if name is not None:
        print(f"Hey {name}!")
    else:
        print("Hello World")
from typing import Union


def say_hi(name: Union[str, None] = None):
    if name is not None:
        print(f"Hey {name}!")
    else:
        print("Hello World")

Union veya Optional kullanmak

Python sürümünüz 3.10'un altındaysa, benim oldukça öznel bakış açıma göre küçük bir ipucu:

  • 🚨 Optional[SomeType] kullanmaktan kaçının
  • Bunun yerine ✨ Union[SomeType, None] kullanın ✨.

İkisi eşdeğerdir ve altta aynı şeydir; ama ben Optional yerine Union önermeyi tercih ederim. Çünkü "optional" kelimesi değerin optional olduğunu ima ediyor gibi durur; ama gerçekte anlamı "değer None olabilir"dir. Değer optional olmasa ve hâlâ required olsa bile.

Bence Union[SomeType, None] ne anlama geldiğini daha açık şekilde ifade ediyor.

Bu, tamamen kelimeler ve isimlendirmelerle ilgili. Ancak bu kelimeler, sizin ve ekip arkadaşlarınızın kod hakkında nasıl düşündüğünü etkileyebilir.

Örnek olarak şu fonksiyonu ele alalım:

from typing import Optional


def say_hi(name: Optional[str]):
    print(f"Hey {name}!")
🤓 Other versions and variants
def say_hi(name: str | None):
    print(f"Hey {name}!")

name parametresi Optional[str] olarak tanımlanmış, ama optional değil; parametre olmadan fonksiyonu çağıramazsınız:

say_hi()  # Oh, no, this throws an error! 😱

name parametresi hâlâ required'dır (optional değildir) çünkü bir default değeri yoktur. Yine de name, değer olarak None kabul eder:

say_hi(name=None)  # This works, None is valid 🎉

İyi haber şu ki, Python 3.10'a geçtiğinizde bununla uğraşmanız gerekmeyecek; çünkü tiplerin union'larını tanımlamak için doğrudan | kullanabileceksiniz:

def say_hi(name: str | None):
    print(f"Hey {name}!")
🤓 Other versions and variants
from typing import Optional


def say_hi(name: Optional[str]):
    print(f"Hey {name}!")

Ve böylece Optional ve Union gibi isimlerle de uğraşmanız gerekmeyecek. 😎

Generic tipler

Köşeli parantez içinde type parameter alan bu tiplere Generic types veya Generics denir, örneğin:

Aynı builtin tipleri generics olarak kullanabilirsiniz (köşeli parantez ve içindeki tiplerle):

  • list
  • tuple
  • set
  • dict

Ve önceki Python sürümlerinde olduğu gibi typing modülünden:

  • Union
  • Optional
  • ...and others.

Python 3.10'da, Union ve Optional generics'lerini kullanmaya alternatif olarak, tip union'larını bildirmek için vertical bar (|) kullanabilirsiniz; bu çok daha iyi ve daha basittir.

Aynı builtin tipleri generics olarak kullanabilirsiniz (köşeli parantez ve içindeki tiplerle):

  • list
  • tuple
  • set
  • dict

Ve typing modülünden gelen generics:

  • Union
  • Optional
  • ...and others.

Tip olarak sınıflar

Bir sınıfı da bir değişkenin tipi olarak bildirebilirsiniz.

Örneğin, adı olan bir Person sınıfınız olsun:

class Person:
    def __init__(self, name: str):
        self.name = name


def get_person_name(one_person: Person):
    return one_person.name

Sonra bir değişkeni Person tipinde olacak şekilde bildirebilirsiniz:

class Person:
    def __init__(self, name: str):
        self.name = name


def get_person_name(one_person: Person):
    return one_person.name

Ve sonra, yine tüm editör desteğini alırsınız:

Bunun "one_person, Person sınıfının bir instance'ıdır" anlamına geldiğine dikkat edin.

"one_person, Person adlı class'tır" anlamına gelmez.

Pydantic modelleri

Pydantic, data validation yapmak için bir Python kütüphanesidir.

Verinin "shape"'ini attribute'lara sahip sınıflar olarak tanımlarsınız.

Ve her attribute'un bir tipi vardır.

Ardından o sınıfın bir instance'ını bazı değerlerle oluşturursunuz; bu değerleri doğrular, uygun tipe dönüştürür (gerekliyse) ve size tüm veriyi içeren bir nesne verir.

Ve bu ortaya çıkan nesne ile tüm editör desteğini alırsınız.

Resmî Pydantic dokümanlarından bir örnek:

from datetime import datetime

from pydantic import BaseModel


class User(BaseModel):
    id: int
    name: str = "John Doe"
    signup_ts: datetime | None = None
    friends: list[int] = []


external_data = {
    "id": "123",
    "signup_ts": "2017-06-01 12:22",
    "friends": [1, "2", b"3"],
}
user = User(**external_data)
print(user)
# > User id=123 name='John Doe' signup_ts=datetime.datetime(2017, 6, 1, 12, 22) friends=[1, 2, 3]
print(user.id)
# > 123
🤓 Other versions and variants
from datetime import datetime
from typing import Union

from pydantic import BaseModel


class User(BaseModel):
    id: int
    name: str = "John Doe"
    signup_ts: Union[datetime, None] = None
    friends: list[int] = []


external_data = {
    "id": "123",
    "signup_ts": "2017-06-01 12:22",
    "friends": [1, "2", b"3"],
}
user = User(**external_data)
print(user)
# > User id=123 name='John Doe' signup_ts=datetime.datetime(2017, 6, 1, 12, 22) friends=[1, 2, 3]
print(user.id)
# > 123

Bilgi

Daha fazlasını öğrenmek için Pydantic'in dokümanlarına bakın.

FastAPI tamamen Pydantic üzerine kuruludur.

Bunların pratikte nasıl çalıştığını Tutorial - User Guide içinde çok daha fazla göreceksiniz.

İpucu

Pydantic, default value olmadan Optional veya Union[Something, None] kullandığınızda özel bir davranışa sahiptir; bununla ilgili daha fazla bilgiyi Pydantic dokümanlarında Required Optional fields bölümünde okuyabilirsiniz.

Metadata Annotations ile Type Hints

Python'da ayrıca, Annotated kullanarak bu type hints içine ek metadata koymayı sağlayan bir özellik de vardır.

Python 3.9'dan itibaren Annotated, standart kütüphanenin bir parçasıdır; bu yüzden typing içinden import edebilirsiniz.

from typing import Annotated


def say_hello(name: Annotated[str, "this is just metadata"]) -> str:
    return f"Hello {name}"

Python'un kendisi bu Annotated ile bir şey yapmaz. Editörler ve diğer araçlar için tip hâlâ str'dir.

Ama FastAPI'ye uygulamanızın nasıl davranmasını istediğinize dair ek metadata sağlamak için Annotated içindeki bu alanı kullanabilirsiniz.

Hatırlanması gereken önemli nokta: Annotated'a verdiğiniz ilk type parameter, gerçek tiptir. Geri kalanı ise diğer araçlar için metadatadır.

Şimdilik, sadece Annotated'ın var olduğunu ve bunun standart Python olduğunu bilmeniz yeterli. 😎

İleride bunun ne kadar güçlü olabildiğini göreceksiniz.

İpucu

Bunun standart Python olması, editörünüzde mümkün olan en iyi developer experience'ı almaya devam edeceğiniz anlamına gelir; kodu analiz etmek ve refactor etmek için kullandığınız araçlarla da, vb. ✨

Ayrıca kodunuzun pek çok başka Python aracı ve kütüphanesiyle çok uyumlu olacağı anlamına gelir. 🚀

FastAPI'de type hints

FastAPI, birkaç şey yapmak için bu type hints'ten faydalanır.

FastAPI ile type hints kullanarak parametreleri bildirirsiniz ve şunları elde edersiniz:

  • Editör desteği.
  • Tip kontrolleri.

...ve FastAPI aynı bildirimleri şunlar için de kullanır:

  • Gereksinimleri tanımlamak: request path parameters, query parameters, headers, bodies, dependencies, vb.
  • Veriyi dönüştürmek: request'ten gerekli tipe.
  • Veriyi doğrulamak: her request'ten gelen veriyi:
    • Veri geçersiz olduğunda client'a dönen otomatik hatalar üretmek.
  • OpenAPI kullanarak API'yi dokümante etmek:
    • bu, daha sonra otomatik etkileşimli dokümantasyon kullanıcı arayüzleri tarafından kullanılır.

Bunların hepsi kulağa soyut gelebilir. Merak etmeyin. Tüm bunları Tutorial - User Guide içinde çalışırken göreceksiniz.

Önemli olan, standart Python tiplerini tek bir yerde kullanarak (daha fazla sınıf, decorator vb. eklemek yerine), FastAPI'nin sizin için işin büyük kısmını yapmasıdır.

Bilgi

Tüm tutorial'ı zaten bitirdiyseniz ve tipler hakkında daha fazlasını görmek için geri döndüyseniz, iyi bir kaynak: mypy'nin "cheat sheet"i.