पायथन टाइप हिंट्स रनटाइम पर मुफ्त हैं, और जब वे झूठ बोलते हैं तो महंगे पड़ते हैं। जो टीमें इनसे मूल्य निकालती हैं, वे हर निजी हेल्पर पर एनोटेशन नहीं ठोकतीं। वे सीम टाइप करती हैं: सार्वजनिक एपीआई, वायर फॉर्मेट, साझा डोमेन ऑब्जेक्ट, और प्लगइन सीमाएँ। बाकी तब तक ढीला रह सकता है जब तक कोई बग या रिफैक्टर फैसला न थोपे।
यह फील्ड गाइड है। typing की हर सुविधा की सूची नहीं, और चेकरों के बीच शुद्धता की होड़ भी नहीं। लक्ष्य: प्रोडक्शन में कम सरप्राइज़, ऐसा बजट जिसे कोड रिव्यू में बचाया जा सके।
"फायदा" का मतलब असल में क्या है
एक हिंट तब फायदा देता है जब वह इनमें से कम से कम एक काम करे:
१. मर्ज से पहले असली बग क्लास रोक दे (जेएसओएन वाले डिक्ट पर गलत की, रीनैम के बाद गायब एट्रिब्यूट, जहाँ स्ट्रिंग चाहिए वहाँ None)।
२. रिफैक्टर सुरक्षित बनाए उन मॉड्यूलों में जो एक साथ दिमाग में नहीं समाते।
३. कॉन्ट्रैक्ट दस्तावेज़ करे जो टेस्ट अकेले नहीं बताते (रिक्वेस्ट बॉडी का आकार, स्टोरेज बैकएंड पर ज़रूरी मेथड)।
४. कोड बदलने पर रखरखाव सस्ता रहे।
अगर एनोटेशन सिर्फ चेकर को खुश रखे, या हर हफ्ते cast माँगे, तो वह हरे सीआई बैज वाली कर्ज़ है।
बीच से नहीं, किनारों से शुरू करें
जब कोडबेस में शून्य टाइप हों, तो यादृच्छिक यूटिलिटी एनोटेट करना सबसे धीमा रास्ता है। जो क्रम काम करता है:
१. सार्वजनिक फ़ंक्शन और मेथड जो पैकेज सीमा पार करते हैं। २. वे डेटा जो प्रोसेस सीमा पार करते हैं: एचटीटीपी बॉडी, क्यू संदेश, कॉन्फ़िग, ओआरएम पंक्तियाँ जिन्हें डिक्ट माना जाता है। ३. इंटरफ़ेस उन कंपोनेंटों के बीच जिन्हें आप बदलते या मॉक करते हैं (स्टोरेज, पेमेंट क्लाइंट, फ़ीचर फ़्लैग)। ४. तब अंदरूनी हिस्सा, जब किनारे ईमानदार हों।
वह सेवा जिसमें निजी हेल्पर पर परफेक्ट टाइप हों और हर फ़ास्टएपीआई हैंडलर पर dict[str, Any] हो, वहाँ टाइप है जहाँ दर्द सबसे कम है।
# किनारा: रिक्वेस्ट अंदर, डोमेन बाहर
def create_invoice(payload: CreateInvoiceRequest, user_id: str) -> Invoice:
...
रिटर्न टाइप पैरामीटर जितने ही महत्वपूर्ण हैं। कॉलर अक्सर आर्ग्युमेंट से ज़्यादा रिटर्न पर गलत अनुमान लगाते हैं।
टाइप्डडिक्ट: पूरी मॉडल लेयर के बिना जेएसओएन और कॉन्फ़िग
जब मॉडल लेयर आपका हो तो पाइडेंटिक और डेटাক्लास बढ़िया हैं। बहुत सा प्रोडक्शन कोड अभी भी json.loads, रेडिस, या थर्ड-पार्टी एसडीके से सादे डिक्ट घुमाता है। वहीं TypedDict अपना किराया कमाता है।
from typing import TypedDict, NotRequired
class UserEvent(TypedDict):
user_id: str
event: str
ts: int
meta: NotRequired[dict[str, str]]
def handle_event(event: UserEvent) -> None:
user_id = event["user_id"] # चेकर जानता है कि की मौजूद है
...
यह क्यों फायदा देता है:
user_idकोaccount_idनाम बदलना हर कॉल साइट पर सीआई में टूटेगा, रात तीन बजे की चुप लॉग लाइन में नहीं।- वैकल्पिक फ़ील्ड
NotRequiredसे साफ़ रहती हैं (पुराने स्टाइल मेंtotal=False)। - पतले एडाप्टर पर पूरी क्लास पदानुक्रम थोपनी नहीं पड़ती।
टाइप्डडिक्ट छोड़ें जब आकार सच में खुला हो (वेंडर वेबहुक जिन्हें आप सिर्फ स्टोर करते हैं), या जब पहले से स्कीमा लाइब्रेरी टाइप जनरेट करती हो। एक ही पेलोड का दोहरा मॉडलिंग व्यर्थ काम है।
नेस्टेड जेएसओएन के लिए दस वैकल्पिक की वाले एक विशाल डिक्ट से बेहतर कुछ छोटे टाइप्डडिक्ट हैं।
प्रोटोकॉल: डक टाइपिंग जो फिर भी जाँचती है
पायथन की ताकत स्ट्रक्चरल टाइपिंग है। Protocol (पीईपी ५४४) वही अंदाज़ रखता है, बिना उन बेस क्लास के जो सिर्फ टाइप चेकर को पसंद आएँ।
from typing import Protocol
class SupportsClose(Protocol):
def close(self) -> None: ...
class UserStore(Protocol):
def get(self, user_id: str) -> User | None: ...
def save(self, user: User) -> None: ...
def shutdown(resource: SupportsClose) -> None:
resource.close()
संगत close() वाली कोई भी वस्तु चलेगी। न एबीसी रजिस्ट्रेशन, न सिर्फ टाइपिंग के लिए साझा निर्भरता।
प्रोडक्शन में कहाँ चमकते हैं:
- पोर्ट और एडाप्टर:
UserStoreपरिभाषित करें, प्रोड में पोस्टग्रेस और टेस्ट में नकली इंजेक्ट करें। - लाइब्रेरी-मित्र एपीआई: ठोस क्लास की जगह "फ़ाइल जैसी कोई भी चीज़" स्वीकारें।
- क्रमिक निकालना: इंटरफ़ेस क्लास निकालने से पहले वे मेथड लिखें जिन्हें आप सच में कॉल करते हैं।
एक ही इम्प्लीमेंटेशन वाली एकमुश्त आंतरिक क्लास पर प्रोटोकॉल छोड़ें। ठोस क्लास एनोटेशन साफ़ है। बीस मेथड वाली "पूर्णता" वाले प्रोटोकॉल से बचें। वही टाइप करें जो कॉलर इस्तेमाल करते हैं।
# अच्छा: छोटा, असली
class Clock(Protocol):
def now(self) -> datetime: ...
# शोर: देवता इंटरफ़ेस जिसे कोई पूरा लागू नहीं करता
class EverythingService(Protocol):
...
यूनियन, ऑप्शनल, और | None (वो बग जो आप सच में शिप करते हैं)
ज्यादातर प्रोडक्शन जीत उबाऊ होती हैं: फ़ंक्शन User | None लौटाता है, कॉलर जाँच भूलता है, चेकर चिल्लाता है।
def find_user(user_id: str) -> User | None:
...
user = find_user(uid)
# name = user.name # error: Item "None" has no attribute "name"
if user is None:
raise LookupError(uid)
name = user.name # नैरो; सुरक्षित
सफलता का नाटक करने वाली खाली वस्तुओं से बेहतर स्पष्ट X | None। प्रोग्रामर गलती पर रेज़ करें, अपेक्षित अनुपस्थिति पर None (या Result)। प्रति कोडबेस एक शैली।
पाँच असंबंधित प्रकारों का Union अक्सर डिज़ाइन गंध है। अगर फ़ंक्शन User | Order | str | int लौटाए, एपीआई तोड़ें।
जेनेरिक वहाँ जहाँ साझा कंटेनर कमाते हैं
जेनेरिक पुन: प्रयोज्य कंटेनर और रिपॉज़िटरी पर फायदा देते हैं, हर लोकल वेरिएबल पर नहीं।
from typing import TypeVar, Generic
T = TypeVar("T")
class Repository(Generic[T]):
def get(self, id: str) -> T | None: ...
def add(self, item: T) -> None: ...
class UserRepository(Repository[User]):
...
या आधुनिक सिंटैक्स और समर्थक चेकर के साथ:
def first[T](items: list[T]) -> T | None:
return items[0] if items else None
गहरे जेनेरिक ग्राफ़ (Repo[T, ID, Filter, Page]) तब तक छोड़ें जब तक असली दर्द न हो। जटिल TypeVar बाउंड वहाँ हैं जहाँ टीमें एक हफ्ता जलाती हैं और उन्हीं बगों को सुंदर सिग्नेचर के साथ शिप करती हैं।
मायपाय और पायराइट: टूल टीम की सेवा करे
पवित्र युद्ध की ज़रूरत नहीं। ज़रूरत है सीआई में एक चेकर की, ऐसी कॉन्फ़िग के साथ जिसे टीम समझा सके।
व्यावहारिक सेटअप:
१. सीआई के लिए एक प्राथमिक चेकर चुनें (वीएस कोड दुकानों में पायराइट/पायलांस आम; पुराने जैंजो/फ़्लास्क मोनोरिपो में मायपाय)। लोकल एडिटर सीआई से मेल खाए।
२. क्रमिक शुरू करें। दस लाख लाइनों वाले लीगेसी पर दिन एक से strict = true टाइपिंग पहल को मार देता है।
३. पैकेज के हिसाब से कसें। कोर डोमेन और सार्वजनिक एपीआई पहले सख्त। स्क्रिप्ट और नोटबुक ढीली।
४. टाइप किए मॉड्यूल की गलतियों पर बिल्ड फेल करें, दिन एक पर सारे थर्ड-पार्टी स्टब ब्रह्मांड पर नहीं।
क्रमिक रोलआउट के लिए pyrightconfig.json का आकार:
{
"include": ["src"],
"exclude": ["**/migrations", "**/scripts"],
"typeCheckingMode": "basic",
"reportMissingImports": true,
"reportOptionalMemberAccess": true
}
बिग बैंग के बिना ईमानदारी की तरफ़ मायपाय उदाहरण:
[mypy]
python_version = 3.12
warn_return_any = True
warn_unused_ignores = True
check_untyped_defs = True
[mypy-src.legacy.*]
ignore_errors = True
चेकर से क्या माँगें
- आपके कोड में एट्रिब्यूट और None गलतियाँ पकड़े।
- गलत टाइप्डडिक्ट की और प्रोटोकॉल बेमेल पकड़े।
- अप्रयुक्त इग्नोर दिखाए ताकि
# type: ignoreस्थायी वॉलपेपर न बने।
किसकी पूजा न करें
- थर्ड-पार्टी की परफेक्ट कवरेज। जहाँ मदद करे स्टब; गंदे एसडीके को पतली टाइप फ़साड के पीछे रखें।
- शून्य
Any। सिस्टम सीमा पर कुछ ईमानदारAnyपचास झूठे "सटीक" टाइप से बेहतर। - यह बहस जीतना कि कौन सा चेकर "ज़्यादा सही" है, जबकि प्रोडक्शन अभी भी हर जगह
dictभेज रहा हो।
cast, Any, और # type: ignore (एस्केप हैच)
ये औज़ार वजह से हैं। दुरुपयोग के पैटर्न:
# बुरा: मॉडलिंग की जगह चुप कराना
user = cast(User, raw_json) # आशा-चालित विकास
data: Any = fetch() # संक्रमण कॉलर तक फैलता है
result = thing.method() # type: ignore[attr-defined]
बेहतर पैटर्न:
def parse_user(raw: dict[str, object]) -> User:
# सीमा पर एक बार वैलिडेट
return User(
id=str(raw["id"]),
email=str(raw["email"]),
)
जो नियम टिकते हैं:
cast: दुर्लभ, स्थानीय, बेहतर हो कि रनटाइम जाँच या टिप्पणी के पास जो बताए चेकर सच्चाई क्यों नहीं देखता।Any: अनटाइप सीमा पर ठीक; क्वारंटीन में रखें। कोर डोमेन सेAnyन लौटाएँ।# type: ignore: कोड के साथ, आदर्श रूप से टिकट या टिप्पणी।warn_unused_ignoresचालू रखें।
क्या जानबूझकर छोड़ें
हर टाइपिंग फीचर प्रोडक्शन रोलआउट का हकदार नहीं:
| फीचर / आदत | कब फायदा | कब छोड़ें |
|---|---|---|
| हर निजी वन-लाइनर एनोटेट करना | लगभग कभी नहीं | डिफ़ॉल्ट छोड़ें |
लीगेसी पर दिन एक पूर्ण strict |
ग्रीनफ़ील्ड या छोटा कोर | किनारों के टाइप होने तक छोड़ें |
| अति-बने जेनेरिक पदानुक्रम | साझा लाइब्रेरी, कलेक्शन | एक उपयोग वाली ऐप कोड |
रनटाइम typing दुरुपयोग |
दुर्लभ वैलिडेशन हेल्पर | हॉट पाथ; रनटाइम जाँच सरल रखें |
| पाइडेंटिक मॉडल की टाइप्डडिक्ट नकल | लागू नहीं | एक स्रोत सत्य |
| एक इम्प्लीमेंटेशन पर प्रोटोकॉल | मल्टी-इम्प्ल पोर्ट | ठोस क्लास काफ़ी |
| फेंकने योग्य नोटबुक/माइग्रेशन टाइप करना | शायद ही | उन्हें अकेला छोड़ें |
ParamSpec / उन्नत कॉलबैक टाइपिंग |
फ़्रेमवर्क और डेकोरेटर लेखक | ज़्यादातर ऐप कोड |
भाषा से लड़ना भी छोड़ें। पायथन रस्ट नहीं बनेगा। मुद्दा सस्ते रिफैक्टर और प्रोड में कम AttributeError है, प्रमेय साबित करने वाला इंजन नहीं।
मौजूदा सेवा के लिए यथार्थवादी रोलआउट
१. एक हफ्ते सीआई में चेकर नॉन-ब्लॉकिंग या सीमित पथ मोड में चालू करें ताकि शोर दिखे।
२. एचटीटीपी/क्यू सीमा टाइप्डडिक्ट या स्कीमा लाइब्रेरी के निर्यात टाइप से टाइप करें।
३. दो-तीन असली पोर्ट (डीबी, कैश, मेलर) पर प्रोटोकॉल जोड़ें और टेस्ट में इस्तेमाल करें।
४. ऑप्शनल मेंबर जाँच चालू करें और None का असर ठीक करें; अकेले यही अक्सर माइग्रेशन का खर्चा निकाल देता है।
५. कोर पैकेज रिव्यू में नई अनटाइप सार्वजनिक फ़ंक्शन पर रोक।
६. प्रति स्प्रिंट एक पैकेज कसें, मरे हुए इग्नोर हटाएँ।
७. मापें: गलत आकार वाले प्रोड बग, फ़ील्ड रीनैम का समय, चेकर बाईपास कितनी बार। अगर ये न हिलें, एनोटेशन नाटक हैं।
वे पैटर्न जो उम्रदराज अच्छी तरह रहते हैं
सीमा पर वैलिडेशन, अंदर विश्वास। अविश्वसनीय इनपुट एक बार टाइप आकार में पार्स करें। ऐप के अंदर dict नहीं, User पास करें।
कंडीशनल पर नैरो करें। if x is None, isinstance, और टैग्ड यूनियन के बाद चेकर नैरोइंग पर भरोसा करें।
आधुनिक पायथन पर list[str] और dict[str, int] (पीईपी ५८५) नए कोड में typing के List/Dict से बेहतर।
एनोटेशन सच्चाई के पास रखें। अगर प्रोड अतिरिक्त फ़ील्ड भेज सकता है, तब तक न दिखावा करें कि टाइप मना करता है जब तक सीमा पर साफ़ न करें।
गैर-स्पष्ट बात सिग्नेचर में लिखें। def price_cents(...) -> int उस टिप्पणी को हराता है जो कहे "सेंट लौटाता है।"
न्यूनतम उदाहरण: वह सीम जो असली गलतियाँ पकड़ती है
from typing import Protocol, TypedDict, NotRequired
class ChargeRequest(TypedDict):
customer_id: str
amount_cents: int
currency: str
idempotency_key: NotRequired[str]
class PaymentGateway(Protocol):
def charge(self, req: ChargeRequest) -> str: ...
def place_order(
gateway: PaymentGateway,
customer_id: str,
amount_cents: int,
) -> str:
req: ChargeRequest = {
"customer_id": customer_id,
"amount_cents": amount_cents,
"currency": "USD",
}
return gateway.charge(req)
टेस्ट डबल को सिर्फ charge चाहिए। amount_cents की टाइपो डिप्लॉय से पहले फेल होती है। स्ट्राइप की जगह नकली लगाने के लिए साझा बेस क्लास नहीं चाहिए। यही प्रोडक्शन टाइपिंग है: छोटे कॉन्ट्रैक्ट, वहाँ लागू जहाँ पैसे और डेटा रेखाएँ पार करते हैं।
निचली पंक्ति
टाइप हिंट्स तब फायदा देते हैं जब वे कॉन्ट्रैक्ट बचाएँ, न कि इम्प्लीमेंटेशन विवरण सजाएँ। प्राथमिकता:
- सार्वजनिक एपीआई और वायर डेटा पर ईमानदार एनोटेशन (
TypedDictया स्कीमा मॉडल) - अदला-बदली सीमाओं पर छोटे प्रोटोकॉल
- None-सुरक्षा और सरल यूनियन जो असली कंट्रोल फ़्लो से मेल खाएँ
- सीआई में एक चेकर, क्रमिक सख्ती, कम एस्केप हैच और उससे भी कम झूठ
जो रस्म बग दर नहीं बदलती उसे छोड़ें। पायथन टाइपिंग बदलते सेवाओं को शिप करने वाली टीमों का औज़ार है। इसे इंजीनियर की तरह इस्तेमाल करें, पीईपी इकट्ठा करने वाले की तरह नहीं।
