2010-03-15 12 views
14

मैं अटक गया हूँ। मैं गतिशील रूप से कक्षा विधि को परिभाषित करने की कोशिश कर रहा हूं और मैं अपने सिर को रूबी मेटाक्लास मॉडल के चारों ओर लपेट नहीं सकता। निम्नलिखित वर्ग पर विचार करें:रूबी मेटाक्लास पागलपन

class Example 

    def self.meta; (class << self; self; end); end 

    def self.class_instance; self; end 

end 

Example.class_instance.class # => Class 
Example.meta.class   # => Class 

Example.class_instance == Example  # => true 
Example.class_instance == Example.meta # => false 

स्पष्ट रूप से दोनों विधियां कक्षा का एक उदाहरण लौटाती हैं। लेकिन इन दो उदाहरण समान नहीं हैं। उनके पास अलग-अलग पूर्वजों भी हैं:

Example.meta.ancestors   # => [Class, Module, Object, Kernel] 
Example.class_instance.ancestors # => [Example, Object, Kernel] 

मेटाक्लास और कक्षा के उदाहरण के बीच अंतर बनाने में क्या बात है?

मुझे पता चला कि मैं send :define_method को गतिशील रूप से एक विधि को परिभाषित करने के लिए मेटाक्लास पर कर सकता हूं, लेकिन यदि मैं इसे कक्षा के उदाहरण में भेजने की कोशिश करता हूं तो यह काम नहीं करेगा। कम से कम मैं अपनी समस्या का समाधान कर सकता हूं, लेकिन मैं अभी भी समझना चाहता हूं कि यह इस तरह क्यों काम कर रहा है।

अद्यतन मार्च 15, 2010 13:40

निम्नलिखित अनुमान सही हैं।

  • यदि मेरे पास एक इंस्टेंस विधि है जो self.instance_eval को कॉल करती है और एक विधि को परिभाषित करती है, तो यह केवल उस वर्ग के विशेष उदाहरण को प्रभावित करेगी।
  • यदि मेरे पास एक उदाहरण विधि है जो self.class.instance_eval (जो क्लास_वेल को कॉल करने के समान ही होगी) को कॉल करती है और एक विधि को परिभाषित करती है तो यह उस विशेष वर्ग के सभी उदाहरणों को प्रभावित करेगी जिसके परिणामस्वरूप एक नई इंस्टेंस विधि होगी।
  • यदि मेरे पास एक क्लास विधि है जो example_eval को कॉल करती है और एक विधि को परिभाषित करती है तो इसके परिणामस्वरूप सभी उदाहरणों के लिए एक नई इंस्टेंस विधि होगी।
  • यदि मेरे पास एक क्लास विधि है जो मेटा/ईजिन क्लास पर instance_eval को कॉल करती है और एक विधि को परिभाषित करती है तो इसका परिणाम क्लास विधि होगा।

मुझे लगता है कि यह मुझे समझ में आता है। यह निश्चित रूप से आपकी संभावनाओं को सीमित करेगा यदि एक वर्ग विधि के भीतर स्वयं ईजिन वर्ग को इंगित करेगा। यदि ऐसा है तो कक्षा विधि के अंदर से एक उदाहरण विधि को परिभाषित करना संभव नहीं होगा। क्या वो सही है?

उत्तर

11

गतिशील रूप से एक सिंगलटन विधि को परिभाषित करना सरल है जब आप का उपयोग instance_eval:

Example.instance_eval{ def square(n); n*n; end } 
Example.square(2) #=> 4 
# you can pass instance_eval a string as well. 
Example.instance_eval "def multiply(x,y); x*y; end" 
Example.multiply(3,9) #=> 27 

अंतर से ऊपर, आप 2 बातें भ्रमित कर रहे हैं के लिए के रूप में:

मेटा वर्ग आप द्वारा परिभाषित किया गया, में क्या कहा जाता है रूबी समुदाय सिंगलटन वर्ग या ईजिन वर्ग के रूप में। वह सिंगलटन वर्ग वह वर्ग है जिसे आप कक्षा (सिंगलटन) विधियों को जोड़ सकते हैं।

वर्ग उदाहरण आप class_instance पद्धति का उपयोग करके परिभाषित करने के लिए कोशिश कर रहे हैं के लिए के रूप में, लेकिन कक्षा में ही कुछ भी नहीं है, यह साबित करने के लिए, बस वर्ग Example के लिए एक उदाहरण विधि जोड़ने का प्रयास करें और जाँच class_instance विधि आप रिटर्न से परिभाषित किया गया है, तो वर्ग Example ही है कि विधि के अस्तित्व की जाँच करके:

class Example 
    def self.meta; (class << self; self; end); end 
    def self.class_instance; self; end 
    def hey; puts hey; end 
end 

Example.class_instance.instance_methods(false) #=> ['hey'] 

वैसे भी, आप के लिए यह योग करने के लिए जब आप वर्ग तरीकों जोड़ना चाहते हैं, सिर्फ इतना है कि मेटा वर्ग में शामिल करें। class_instance विधि के लिए बेकार है, बस इसे हटा दें।

वैसे भी मुझे सुझाव है कि आप रुबी प्रतिबिंब प्रणाली की कुछ अवधारणाओं को समझने के लिए this post पढ़ लें।

अद्यतन

मैं सुझाव है कि आप यह अच्छा पोस्ट पढ़ें: Fun with Ruby's instance_eval and class_eval, दुर्भाग्य class_eval और instance_eval भ्रमित कर रहे हैं, क्योंकि वे किसी तरह अपने नामकरण के खिलाफ काम करते हैं!

Use ClassName.instance_eval to define class methods. 

Use ClassName.class_eval to define instance methods. 

अब अपनी मान्यताओं का जवाब दे:

अगर मैं एक उदाहरण विधि है जो कॉल self.instance_eval और एक पद्धति निर्धारित करता है है, यह केवल उस वर्ग के विशेष उदाहरण को प्रभावित करेगा।

हाँ:

class Foo 
    def assumption1() 
    self.instance_eval("def test_assumption_1; puts 'works'; end") 
    end 
end 

f1 = Foo.new 
f1.assumption1 
f1.methods(false) #=> ["test_assumption_1"] 
f2 = Foo.new.methods(false) #=> [] 

अगर मैं एक उदाहरण विधि है जो कॉल self.class.instance_eval (जो class_eval कॉल करने जैसा ही होगा) और एक विधि यह होगा परिभाषित करता है विशेष वर्ग के सभी उदाहरणों को प्रभावित करते हैं जिसके परिणामस्वरूप एक नया इंस्टेंस विधि होता है।

वर्ग पर ही इस संदर्भ सिंगलटन तरीकों परिभाषित करेगा (नहीं उदाहरण वाले) में कोई instance_eval:

class Foo 
    def assumption2() 
    self.class.instance_eval("def test_assumption_2; puts 'works'; end") 
    end 
end 

f3 = Foo.new 
f3.assumption2 
f3.methods(false) #=> [] 
Foo.singleton_methods(false) #=> ["test_assumption_2"] 

कि के लिए ऊपर class_eval साथ instance_eval की जगह काम करने के लिए।

अगर मैं एक वर्ग विधि है जो instance_eval कॉल करता है और एक विधि यह सभी उदाहरणों के लिए एक नया उदाहरण विधि में परिणाम होगा परिभाषित करता है।

नहीं:

class Foo 
    instance_eval do 
    def assumption3() 
     puts 'works' 
    end 
    end 
end 

Foo.instance_methods(false) #=> [] 

Foo.singleton_methods(false) #=> ["assumption_3"] 

कि सिंगलटन तरीकों, नहीं उदाहरण के तरीकों कर देगा। इसके लिए instance_eval को class_eval के साथ प्रतिस्थापित करने के लिए काम करना है।

अगर मैं एक वर्ग विधि है जो मेटा/eigen वर्ग पर instance_eval कॉल करता है और एक विधि यह एक वर्ग विधि का परिणाम देगा परिभाषित करता है।

अच्छी तरह से, यह इतना परिष्कृत सामान बना देगा, क्योंकि यह सिंगलटन वर्ग में सिंगलटन विधि जोड़ देगा, मुझे नहीं लगता कि इसका कोई व्यावहारिक उपयोग होगा।

+0

'example_eval' के अंदर _why_' 'def' के बारे में और जानकारी के लिए कक्षा विधियों को परिभाषित करता है, यह आलेख देखें http://yugui.jp/articles/846 – horseyguy

+0

बहुत बहुत धन्यवाद। मैं अपना प्रश्न अपडेट करता हूं। क्या आप इसे देखने पर ध्यान देंगे? – t6d

+0

आपके अपडेट में सवालों के जवाब देने के लिए अपडेट किया गया। – khelll

5

आप एक वर्ग पर एक विधि को परिभाषित करते हैं, तो वह अपने वस्तुओं पर लागू किया जा सकता है। यह उदाहरण विधि है।

class Example 
end 

Example.send :define_method, :foo do 
    puts "foo" 
end 

Example.new.foo 
#=> "foo" 

आप एक metaclass पर एक विधि को परिभाषित करते हैं, यह वर्ग पर लागू किया जा सकता है। यह वर्ग विधि या अन्य भाषाओं में स्थैतिक विधि की अवधारणा के समान है।

class Example 
    def self.metaclass 
    class << self 
     self 
    end 
    end 
end 

Example.metaclass.send :define_method, :bar do 
    puts "bar" 
end 

Example.bar 
#=> "bar" 

कारण कि metaclasses मौजूद है, क्योंकि आप रूबी में ऐसा कर सकते हैं:

str = "hello" 
class << str 
    def output 
    puts self 
    end 
end 

str.output 
#=> "hello" 

"hi".output 
# NoMethodError 

आप देख सकते हैं, हम एक विधि है कि केवल एक का एक उदाहरण के लिए उपलब्ध है परिभाषित स्ट्रिंग। जिस चीज को हमने इस विधि को परिभाषित किया है उसे मेटाक्लास कहा जाता है। विधि लुकअप श्रृंखला में, वस्तु वर्ग की खोज करने से पहले मेटाक्लास को पहले एक्सेस किया जाता है।

अगर हम प्रकार Class का एक उद्देश्य के साथ प्रकार String की वस्तु की जगह, आप कल्पना कर सकते क्यों यह मतलब है कि हम केवल एक विशिष्ट वर्ग पर एक विधि को परिभाषित कर रहे हैं, सभी वर्गों पर नहीं।

वर्तमान संदर्भ और self के बीच अंतर सूक्ष्म हैं, यदि आप रुचि रखते हैं तो आप read more कर सकते हैं।

संबंधित मुद्दे