रूबी मैजिक के एक नए एपिसोड में आपका स्वागत है! इस महीने का संस्करण मेटाक्लासेस के बारे में है, एक विषय जो दो डेवलपर्स (हाय मौड!) के बीच चर्चा से उत्पन्न हुआ।
मेटाक्लासेस की जांच करके, हम सीखेंगे कि रूबी में क्लास और इंस्टेंस विधियाँ कैसे काम करती हैं। रास्ते में, एक स्पष्ट "डिफाइनी" पास करके, class << self या instance_eval का उपयोग करके एक विधि को परिभाषित करने के बीच अंतर की खोज करें। चलते हैं!
क्लास इंस्टेंसेस और इंस्टेंस विधियाँ
यह समझने के लिए कि रूबी में मेटाक्लासेस का उपयोग क्यों किया जाता है, हम इंस्टेंस- और क्लास विधियों के बीच अंतरों की जांच करके शुरू करेंगे।
रूबी में, एक क्लास एक ऑब्जेक्ट है जो अन्य ऑब्जेक्ट्स बनाने के लिए एक ब्लूप्रिंट को परिभाषित करता है। क्लासेस परिभाषित करती हैं कि उस क्लास के किसी भी इंस्टेंस पर कौन सी विधियाँ उपलब्ध हैं।
क्लास के अंदर एक विधि को परिभाषित करने से उस क्लास पर एक इंस्टेंस विधि बनती है। उस क्लास का कोई भी भविष्य का इंस्टेंस उस विधि को उपलब्ध कराएगा।
class User def initialize(name) @name = name end def name @name end end user = User.new('Thijs') user.name # => "Thijs"इस उदाहरण में, हम User नामक एक क्लास बनाते हैं, जिसमें #name नामक एक इंस्टेंस विधि होती है जो उपयोगकर्ता का नाम लौटाती है। क्लास का उपयोग करके, हम फिर एक क्लास इंस्टेंस बनाते हैं और इसे user नामक वेरिएबल में स्टोर करते हैं। चूंकि user User क्लास का एक इंस्टेंस है, इसलिए इसमें #name विधि उपलब्ध है।
एक क्लास अपने इंस्टेंस विधियों को अपने मेथड टेबल में स्टोर करती है। उस क्लास का कोई भी इंस्टेंस अपने इंस्टेंस विधियों तक पहुंच प्राप्त करने के लिए अपनी क्लास के मेथड टेबल को संदर्भित करता है।
क्लास ऑब्जेक्ट्स
एक क्लास विधि एक विधि है जिसे पहले इंस्टेंस बनाए बिना सीधे क्लास पर कॉल किया जा सकता है। एक क्लास विधि को इसके नाम को self. से प्रीफिक्स करके बनाया जाता है जब इसे परिभाषित किया जाता है।
एक क्लास स्वयं एक ऑब्जेक्ट है। एक कांस्टेंट क्लास ऑब्जेक्ट को संदर्भित करता है, इसलिए इस पर परिभाषित क्लास विधियों को एप्लिकेशन में कहीं से भी कॉल किया जा सकता है।
class User # ... def self.all [new("Thijs"), new("Robert"), new("Tom")] end end User.all # => [#<User:0x00007fb01701efb8 @name="Thijs">, #<User:0x00007fb01701ef68 @name="Robert">, #<User:0x00007fb01701ef18 @name="Tom">]self.-प्रीफिक्स के साथ परिभाषित विधियों को क्लास के मेथड टेबल में नहीं जोड़ा जाता है। उन्हें इसके बजाय क्लास के मेटाक्लास में जोड़ा जाता है।
मेटाक्लासेस
एक क्लास के अलावा, रूबी में प्रत्येक ऑब्जेक्ट का एक छिपा हुआ मेटाक्लास होता है। मेटाक्लासेस सिंगलटन होते हैं, जिसका अर्थ है कि वे एक ही ऑब्जेक्ट से संबंधित होते हैं। यदि आप एक क्लास के कई इंस्टेंस बनाते हैं, तो वे उसी क्लास को साझा करेंगे, लेकिन उन सभी के अलग-अलग मेटाक्लासेस होंगे।
thijs, robert, tom = User.all thijs.class # => User robert.class # => User tom.class # => User thijs.singleton_class # => #<Class:#<User:0x00007fb71a9a2cb0>> robert.singleton_class # => #<Class:#<User:0x00007fb71a9a2c60>> tom.singleton_class # => #<Class:#<User:0x00007fb71a9a2c10>>इस उदाहरण में, हम देखते हैं कि हालांकि प्रत्येक ऑब्जेक्ट की क्लास User है, उनके सिंगलटन क्लासेस के अलग-अलग ऑब्जेक्ट आईडी हैं, जिसका अर्थ है कि वे अलग ऑब्जेक्ट्स हैं।
मेटाक्लास तक पहुंच होने से, रूबी मौजूदा ऑब्जेक्ट्स में सीधे विधियों को जोड़ने की अनुमति देती है। ऐसा करने से ऑब्जेक्ट की क्लास में कोई नई विधि नहीं जुड़ेगी।
robert = User.new("Robert") def robert.last_name "Beekman" end robert.last_name # => "Beekman" User.new("Tom").last_name # => NoMethodError (undefined method `last_name' for #<User:0x00007fe1cb116408>)इस उदाहरण में, हम robert वेरिएबल में संग्रहीत उपयोगकर्ता में एक #last_name जोड़ते हैं। हालांकि robert User का एक इंस्टेंस है, User के किसी भी नए बनाए गए इंस्टेंस की #last_name विधि तक पहुंच नहीं होगी, क्योंकि यह केवल robert के मेटाक्लास पर मौजूद है।
self क्या है?
जब एक विधि को परिभाषित करते समय एक रिसीवर पास करते हैं, तो नई विधि क्लास के मेथड टेबल में जोड़ने के बजाय रिसीवर के मेटाक्लास में जोड़ी जाती है।
tom = User.new("Tom") def tom.last_name "de Bruijn" endऊपर के उदाहरण में, हमने tom ऑब्जेक्ट पर सीधे #last_name जोड़ा है, विधि को परिभाषित करते समय tom को रिसीवर के रूप में पास करके।
यह क्लास विधियों के लिए भी इसी तरह काम करता है।
class User # ... def self.all [new("Thijs"), new("Robert"), new("Tom")] end endयहां, हम .all विधि बनाते समय रिसीवर के रूप में स्पष्ट रूप से self पास करते हैं। एक क्लास परिभाषा में, self क्लास (User इस मामले में) को संदर्भित करता है, इसलिए .all विधि User के मेटाक्लास में जुड़ जाती है।
क्योंकि User एक कांस्टेंट में स्टोर किया गया एक ऑब्जेक्ट है, हम जब भी इसे संदर्भित करते हैं तो उसी ऑब्जेक्ट — और उसी मेटाक्लास — को एक्सेस करेंगे।
मेटाक्लास खोलना
हमने सीखा है कि क्लास विधियाँ क्लास ऑब्जेक्ट के मेटाक्लास में विधियाँ हैं। यह जानकर, हम क्लास विधियाँ बनाने की कुछ अन्य तकनीकों को देखेंगे जो आपने पहले देखी होंगी।
class << self
हालांकि यह थोड़ा आउट ऑफ स्टाइल हो गया है, कुछ लाइब्रेरीज़ क्लास विधियों को परिभाषित करने के लिए class << self का उपयोग करती हैं। यह सिंटैक्स ट्रिक वर्तमान क्लास के मेटाक्लास को खोलता है और इसके साथ सीधे इंटरैक्ट करता है।
class User class << self self # => #<Class:User> def all [new("Thijs"), new("Robert"), new("Tom")] end end end User.all # => [#<User:0x00007fb01701efb8 @name="Thijs">, #<User:0x00007fb01701ef68 @name="Robert">, #<User:0x00007fb01701ef18 @name="Tom">]यह उदाहरण User के मेटाक्लास में एक विधि जोड़कर User.all नामक एक क्लास विधि बनाता है। पिछले दृष्टिकोण के विपरीत जहां हमने विधि के लिए स्पष्ट रूप से एक रिसीवर पास किया था, हम self को User के मेटाक्लास पर सेट करते हैं बजाय User के स्वयं।
जैसा कि हमने पहले सीखा, स्पष्ट रिसीवर के बिना कोई भी विधि परिभाषा वर्तमान क्लास के इंस्टेंस विधि के रूप में जुड़ जाती है। ब्लॉक के अंदर, वर्तमान क्लास User का मेटाक्लास है (#<Class:User>)।
instance_eval
एक अन्य विकल्प instance_eval का उपयोग करना है, जो एक प्रमुख अंतर के साथ वही काम करता है। हालांकि क्लास का मेटाक्लास ब्लॉक में परिभाषित विधियों को प्राप्त करता है, self मुख्य क्लास का संदर्भ बना रहता है।
class User instance_eval do self # => User def all [new("Thijs"), new("Robert"), new("Tom")] end end end User.all # => [#<User:0x00007fb01701efb8 @name="Thijs">, #<User:0x00007fb01701ef68 @name="Robert">, #<User:0x00007fb01701ef18 @name="Tom">]इस उदाहरण में, हम User के मेटाक्लास पर एक इंस्टेंस विधि को ठीक पहले की तरह परिभाषित करते हैं, लेकिन self अभी भी User को इंगित करता है। हालांकि यह आमतौर पर उसी ऑब्जेक्ट को इंगित करता है, "डिफ़ॉल्ट डिफाइनी" और self अलग-अलग ऑब्जेक्ट्स को इंगित कर सकते हैं।
हमने क्या सीखा
हमने सीखा है कि क्लासेस एकमात्र ऑब्जेक्ट्स हैं जिनमें विधियाँ हो सकती हैं, और इंस्टेंस विधियाँ वास्तव में एक ऑब्जेक्ट के मेटाक्लास पर विधियाँ हैं। हम जानते हैं कि class << self केवल self को स्वैप करता है ताकि आप मेटाक्लास पर विधियों को परिभाषित कर सकें, और हम जानते हैं कि instance_eval ज्यादातर वही काम करता है (लेकिन self को छुए बिना)।
हालांकि आप स्पष्ट रूप से मेटाक्लासेस के साथ काम नहीं करेंगे, रूबी उनका हुड के नीचे बड़े पैमाने पर उपयोग करती है। यह जानना कि जब आप एक विधि को परिभाषित करते हैं तो क्या होता है, आपको यह समझने में मदद कर सकता है कि रूबी ऐसा व्यवहार क्यों करती है (और आपको क्लास विधियों को self. से प्रीफिक्स क्यों करना पड़ता है)।
पढ़ने के लिए धन्यवाद। यदि आपको जो पढ़ा वह पसंद आया, तो आप रूबी मैजिक की सदस्यता लेना पसंद कर सकते हैं ताकि जब हम लगभग एक महीने में एक नया लेख प्रकाशित करें तो एक ईमेल प्राप्त करें।