2025, Oct 06 19:31
ttk.Widget वास्तव में tkinter.Widget से कैसे इनहेरिट करता है
लेख बताता है कि ttk.Widget, tkinter.Widget से इनहेरिट करता है; नामों के ओवरलैप का भ्रम कैसे दूर करें, पूरी तरह क्वालिफाइड नाम से सही सबक्लासिंग और स्टाइलिंग समझें.
tkinter का सोर्स पहली नज़र में उलझन पैदा कर सकता है, खासकर जब दो क्लासों का नाम एक जैसा हो. एक आम अड़चन तब आती है जब ttk में class Widget(tkinter.Widget) दिखता है और लगता है कि वह खुद को ही बढ़ा रहा है. ऐसा नहीं है. असल में क्या होता है और ऐसा डिज़ाइन क्यों किया गया है, यही आगे समझाया गया है.
उलझन आती कहाँ से है
ttk मॉड्यूल tkinter पैकेज को इम्पोर्ट करता है और फिर एक Widget घोषित करता है जो tkinter.Widget का सबक्लास है. अगर आप सिर्फ ttk मॉड्यूल को देखें, तो ऐसा लग सकता है कि कोई दूसरा Widget मौजूद ही नहीं और ttk.Widget खुद से ही इनहेरिट कर रहा है. अहम बात यह है कि tkinter अपने पैकेज की __init__.py फाइल में एक बेस Widget क्लास पहले से परिभाषित करता है, और ttk उसी पर आगे की परत जोड़ता है.
वही न्यूनतम कोड जो सवाल उठाता है
यह भ्रम पैदा करने वाली संरचना को नीचे दिए पैटर्न तक समेटा जा सकता है:
import tkinter as ui
class Panel(ui.Widget):
    """Base class for themed widgets."""
    pass
ऊपर-ऊपर देखने पर यह चक्रीय लग सकता है. पर ऐसा नहीं है, क्योंकि ui.Widget उस क्लास को संदर्भित करता है जो पहले से ही tkinter पैकेज में मौजूद है.
हकीकत में क्या हो रहा है
tkinter पैकेज के भीतर __init__.py में एक बेस विजेट क्लास परिभाषित है. अवधारणा के स्तर पर वह कुछ यूँ दिखता है:
class BaseItem(CoreBase, PackMixin, PlaceMixin, GridMixin):
    pass
इसका उद्देश्य इस तरह बताया गया है:
आंतरिक क्लास. वह बेस क्लास, जिसके विजेट को Pack, Place या Grid जियोमेट्री मैनेजर से स्थित किया जा सकता है.
इसके बाद ttk की परत उसी बेस क्लास से सबक्लास बनाती है. यानी ttk.Widget, tkinter.Widget से इनहेरिट करता है, अपने आप से नहीं. यही वजह है कि ttk को आमतौर पर from tkinter import ttk के जरिए इम्पोर्ट किया जाता है: ttk, tkinter के ऊपर बना है और उसके विजेट्स—खासतौर पर उनकी स्टाइलिंग क्षमताओं—को आगे बढ़ाता है.
जो इनहेरिटेंस अभिप्रेत है
ttk में इनहेरिटेंस का रिश्ता कुछ इस तरह रेखांकित किया जा सकता है:
import tkinter as ui
class StyledPanel(ui.Widget):
    """Base class for themed widgets."""
    def __init__(self, parent, kind, opts=None):
        parent = init_container(parent)
        ui.Widget.__init__(self, parent, kind, kw=opts)
लॉजिक मूल प्रवाह जैसा ही है: tkinter पैकेज इम्पोर्ट करो, उसकी Widget से डेरिव करो, पैरेंट को सामान्य रूप में लाओ, फिर बेस क्लास का इनिशियलाइज़र कॉल करो.
समाधान: पूरी तरह क्वालिफाइड नाम पर ध्यान दें
यहाँ कोई बग नहीं जिसे ठीक करना हो—बस नामों का ओवरलैप समझना है. कुंजी है पूरी तरह क्वालिफाइड नाम. tkinter.Widget, tkinter पैकेज की __init__.py में परिभाषित क्लास को संदर्भित करता है. ttk.Widget एक अलग क्लास है जो उसी से सबक्लास बनती है. नाम समान हैं, मॉड्यूल अलग—इसलिए चक्रीय इनहेरिटेंस नहीं है.
यह क्यों मायने रखता है
इस लेयरिंग को समझना स्टैंडर्ड लाइब्रेरी में नेविगेट करते समय और अपनी सबक्लास बनाते वक्त काम आता है. आपको जिस व्यवहार और स्टाइलिंग का आधार चाहिए, उसके मुताबिक आप अपनी क्लास tkinter.Widget से, ttk.Widget से, या किसी से भी नहीं—किसी और बेस से—डेरिव कर सकते हैं. यह एक व्यावहारिक पाठ भी یاد दिलाता है: अलग-अलग मॉड्यूल में एक ही क्लास-नाम दोबारा इस्तेमाल करना वैध है, पर अगर आप मॉड्यूल क्वालिफायर पर ध्यान न दें तो कोड समझने की रफ्तार धीमी हो सकती है.
सारांश
जब tkinter और ttk के कोड में class X(Y.Z) जैसा कुछ दिखे, तो क्वालिफायर ध्यान से पढ़ें. ttk, tkinter पैकेज के भीतर मौजूद उसकी बेस Widget को सबक्लास करके उस पर निर्माण करता है. यह डिज़ाइन सोच-समझकर चुना गया है और लेयर्ड लाइब्रेरियों में आम है. अपनी कोडबेस में ऐसे पैटर्न अपनाते समय साफ-सुथरे, पूरी तरह क्वालिफाइड रेफ़रेंस इस्तेमाल करें, और जरूरत हो तो अलग नाम चुनें ताकि हायरार्की आसानी से समझ आए.
यह लेख StackOverflow के प्रश्न, जिसे Darien Marks ने पूछा, और JRiggles के उत्तर पर आधारित है।