2008-09-30 13 views
35

में एक कक्षा में एक आवृत्ति चर जोड़ना रनटाइम पर परिभाषित कक्षा में एक आवृत्ति चर कैसे जोड़ सकता है, और बाद में कक्षा के बाहर से अपना मान प्राप्त कर सकता है?रूबी

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

+1

यह स्पष्ट नहीं किया जा सकता है, लेकिन आप क्लास क्लासनाम के साथ किसी भी समय कक्षा को सचमुच फिर से खोल सकते हैं; (यहां नई चीजें); समाप्त। यह एक मौजूदा वर्ग में सामान जोड़ता है। साफ, हुह? – webmat

+0

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

उत्तर

15

आप विशेषता accessors का उपयोग कर सकते हैं:

array = [] 
array.var = 123 
puts array.var 
:

class Array 
    attr_accessor :var 
end 

अब आप इसे माध्यम से उपयोग कर सकते हैं


ध्यान दें कि आप भी attr_reader या attr_writer उपयोग कर सकते हैं बस ही टिककर खेल या setters परिभाषित करने के लिए या आप जैसे उन्हें मैन्युअल रूप से परिभाषित कर सकते हैं:

class Array 
    attr_reader :getter_only_method 
    attr_writer :setter_only_method 

    # Manual definitions equivalent to using attr_reader/writer/accessor 
    def var 
    @var 
    end 

    def var=(value) 
    @var = value 
    end 
end 

तुम भी सिंगलटन तरीकों का उपयोग कर सकते हैं अगर आप सिर्फ यह एक एकल पर परिभाषित चाहते उदाहरण:

array = [] 

def array.var 
    @var 
end 

def array.var=(value) 
    @var = value 
end 

array.var = 123 
puts array.var 

FYI करें, इस जवाब पर टिप्पणी के जवाब में, सिंगलटन विधि ठीक काम करता है, और निम्न सबूत है:

irb(main):001:0> class A 
irb(main):002:1> attr_accessor :b 
irb(main):003:1> end 
=> nil 
irb(main):004:0> a = A.new 
=> #<A:0x7fbb4b0efe58> 
irb(main):005:0> a.b = 1 
=> 1 
irb(main):006:0> a.b 
=> 1 
irb(main):007:0> def a.setit=(value) 
irb(main):008:1> @b = value 
irb(main):009:1> end 
=> nil 
irb(main):010:0> a.setit = 2 
=> 2 
irb(main):011:0> a.b 
=> 2 
irb(main):012:0> 

आप देख सकते हैं, सिंगलटन विधि setit, एक ही क्षेत्र, @b सेट हो जाएगा के रूप में एक attr_accessor का उपयोग करके परिभाषित ... इसलिए एक सिंगलटन विधि इस सवाल का एक पूरी तरह से वैध तरीका है।

+0

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

+1

यहां देखें कि क्यों समझाया गया है: http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html – Sixty4Bit

+0

यह आपको वह परिणाम देगा जो आप ढूंढ रहे हैं ... मैंने अभी इसका परीक्षण किया ..... –

62

रूबी इसके लिए विधियों, instance_variable_get और instance_variable_set प्रदान करता है। (docs)

आप इस तरह बना सकते हैं और असाइन कर सकते हैं एक नया उदाहरण चर:

>> foo = Object.new 
=> #<Object:0x2aaaaaacc400> 

>> foo.instance_variable_set(:@bar, "baz") 
=> "baz" 

>> foo.inspect 
=> #<Object:0x2aaaaaacc400 @bar=\"baz\"> 
+0

यह काम करता है यदि आप इसे कक्षा के किसी विशेष उदाहरण के लिए चाहते हैं ... लेकिन मैं उन तरीकों का उपयोग करने के बजाय उदाहरण पर सिंगलटन विधियों को परिभाषित करना पसंद करूंगा –

+3

माइक, मुझे लगता है कि यह इस प्रश्न का वास्तविक लक्ष्य है। वह पूछ रहा है कि "रनटाइम पर" एक चर जोड़ने के लिए, इसलिए कोड में इसे परिभाषित करने के बजाय example_variable_set का उपयोग करें। संभावना "बाज़" से भी अधिक एक और चर होगा। – Sixty4Bit

+0

सिंगलटन विधि अभी भी वही चीज़ प्राप्त करेगी, मेरे उत्तर के नीचे मेरा आईआरबी आउटपुट देखें। –

2

Mike Stone's answer पहले से ही काफी व्यापक है, लेकिन मैं थोड़ा विस्तार जोड़ना चाहता हूं।

कुछ उदाहरणों के बाद भी, आप किसी भी समय अपनी कक्षा को संशोधित कर सकते हैं, और परिणाम प्राप्त कर सकते हैं। आप अपने कंसोल में इसे आज़माने कर सकते हैं:

s1 = 'string 1' 
s2 = 'string 2' 

class String 
    attr_accessor :my_var 
end 

s1.my_var = 'comment #1' 
s2.my_var = 'comment 2' 

puts s1.my_var, s2.my_var 
0

केवल पढ़ने के लिए, अपने संपादित के जवाब में:

संपादित करें: ऐसा लगता है कि मैं है कि मैं एक metaprogramming समाधान के लिए देख रहा हूँ स्पष्ट करने की आवश्यकता जो के बजाय मूल रूप से कक्षा को परिभाषित करने वाले स्रोत कोड को संशोधित करने के बजाय कक्षा उदाहरण को रनटाइम पर संशोधित करने की अनुमति देता है। में से कुछ समाधान बताते हैं कि कक्षा परिभाषाओं में उदाहरण चर को कैसे घोषित किया जाए, लेकिन यह नहीं है कि मैं पूछ रहा हूं। गलतफहमी के लिए खेद है।

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

class A 
    def hello 
    print "hello " 
    end 
end 

class A 
    def world 
    puts "world!" 
    end 
end 

a = A.new 
a.hello 
a.world 

ऊपर पूरी तरह से वैध रूबी कोड है, और 2 वर्ग परिभाषाओं कई रूबी फ़ाइलों में फैल सकता है। क्लास इंस्टेंस पर एक नई विधि को परिभाषित करने के लिए आप मॉड्यूल ऑब्जेक्ट में "define_method" विधि का उपयोग कर सकते हैं, लेकिन यह खुली कक्षाओं का उपयोग करने के बराबर है।

रूबी में "ओपन वर्ग" आप किसी भी समय ... नए तरीकों मौजूदा तरीकों जोड़ने के लिए, फिर से परिभाषित मतलब है, या जो भी आप वास्तव में चाहते हैं, जिस पर किसी भी वर्ग को फिर से परिभाषित कर सकते हैं। ऐसा लगता है कि "ओपन क्लास" समाधान वास्तव में आप जो खोज रहे हैं ...

2

अन्य समाधान पूरी तरह से काम करेंगे, लेकिन यहां _ विधि को परिभाषित करने का एक उदाहरण है, यदि आप खुले उपयोग नहीं कर रहे हैं कक्षाएं ... यह सरणी वर्ग के लिए "var" चर परिभाषित करेगा ... लेकिन ध्यान दें कि यह एक खुला वर्ग का उपयोग कर के बराबर है ... लाभ आप ऐसा किसी भी वस्तु की क्लास एक अज्ञात वर्ग के लिए यह कर सकते हैं (है, बल्कि एक विशिष्ट वर्ग) खोलने की तुलना में ... भी _ विधि को परिभाषित एक विधि के अंदर काम करेंगे, जबकि आप एक विधि के भीतर एक वर्ग नहीं खोल सकता।

array = [] 
array.class.send(:define_method, :var) { @var } 
array.class.send(:define_method, :var=) { |value| @var = value } 

और यहाँ यह के उपयोग का एक उदाहरण है ... ध्यान दें कि array2, एक विभिन्न सरणी भी, विधियों है, इसलिए यदि यह नहीं है कि आप क्या चाहते, तो आप शायद सिंगलटन तरीकों जो मैं चाहता हूँ में विस्तार से बताया एक और पोस्ट

irb(main):001:0> array = [] 
=> [] 
irb(main):002:0> array.class.send(:define_method, :var) { @var } 
=> #<Proc:[email protected](irb):2> 
irb(main):003:0> array.class.send(:define_method, :var=) { |value| @var = value } 
=> #<Proc:[email protected](irb):3> 
irb(main):004:0> array.var = 123 
=> 123 
irb(main):005:0> array.var 
=> 123 
irb(main):006:0> array2 = [] 
=> [] 
irb(main):007:0> array2.var = 321 
=> 321 
irb(main):008:0> array2.var 
=> 321 
irb(main):009:0> array.var 
=> 123 
15

@Readonly

"वर्ग MyObject" के आपके उपयोग एक खुले वर्ग के एक उपयोग है, तो कृपया ध्यान दें कि आप इनिशियलाइज़ विधि को पुनर्परिभाषित कर रहे हैं।

रूबी में, ओवरलोडिंग जैसी कोई चीज़ नहीं है ... केवल ओवरराइडिंग, या फिर से परिभाषा ... दूसरे शब्दों में केवल किसी दिए गए विधि का 1 उदाहरण हो सकता है, इसलिए यदि आप इसे फिर से परिभाषित करते हैं, तो इसे फिर से परिभाषित किया जाता है .. और प्रारंभिक विधि अलग नहीं है (भले ही क्लास ऑब्जेक्ट्स का नया तरीका उपयोग किया जाए)।

इस प्रकार, पहले इसे अलियासिंग किए बिना किसी मौजूदा विधि को फिर से परिभाषित न करें ... कम से कम यदि आप मूल परिभाषा तक पहुंच चाहते हैं। और एक अज्ञात वर्ग की प्रारंभिक विधि को फिर से परिभाषित करना काफी जोखिम भरा हो सकता है।

किसी भी दर पर, मुझे लगता है कि मैं तुम्हारे लिए एक बहुत सरल समाधान है, जो सिंगलटन पद्धतियां निर्धारित करने के वास्तविक metaclass का उपयोग करता है:

m = MyObject.new 
metaclass = class << m; self; end 
metaclass.send :attr_accessor, :first, :second 
m.first = "first" 
m.second = "second" 
puts m.first, m.second 

तुम भी जटिल काम पाने के लिए दोनों metaclass और खुले कक्षाओं का उपयोग कर सकते हैं और "metaclass" विधि के माध्यम से

class MyObject 
    def metaclass 
    class << self 
     self 
    end 
    end 

    def define_attributes(hash) 
    hash.each_pair { |key, value| 
     metaclass.send :attr_accessor, key 
     send "#{key}=".to_sym, value 
    } 
    end 
end 

m = MyObject.new 
m.define_attributes({ :first => "first", :second => "second" }) 

ऊपर मूल रूप से उजागर कर रहा है metaclass, तो गतिशील attr _ acce के साथ गुण का एक समूह को परिभाषित करने का उपयोग कर इसे में _ परिभाषित विशेषताओं: की तरह कुछ करना ssor, और उसके बाद हैश में संबंधित मान के साथ विशेषता सेटटर का आविष्कार किया।

रूबी के साथ आप रचनात्मक होना होगा और क्या कर सकते हैं एक ही बात कई अलग अलग तरीकों ;-)


FYI करें, मामले में आप नहीं जानते थे, metaclass का उपयोग कर के रूप में मैंने किया है आप केवल कार्य कर रहे हैं इसका मतलब है वस्तु के दिए गए उदाहरण पर। इस प्रकार, _ विशेषताओं को परिभाषित करने के लिए विशेषताओं को केवल उस विशेष उदाहरण के लिए उन विशेषताओं को परिभाषित किया जाएगा।

उदाहरण:

m1 = MyObject.new 
m2 = MyObject.new 
m1.define_attributes({:a => 123, :b => 321}) 
m2.define_attributes({:c => "abc", :d => "zxy"}) 
puts m1.a, m1.b, m2.c, m2.d # this will work 
m1.c = 5 # this will fail because c= is not defined on m1! 
m2.a = 5 # this will fail because a= is not defined on m2! 
+0

क्या यह इन विधियों को MyObject ऑब्जेक्ट को परिभाषित नहीं करेगा, न कि क्लास स्वयं? तो यह एक m1.a, m1.b को परिभाषित करेगा जहां ए और बी केवल उस ऑब्जेक्ट के लिए सिंगलटन विधियां हैं। – vrish88

+0

@ माइक मुझे आपके पहले कोड स्निपेट पर '

' में निजी विधि 'पहली =' # # MyClass: 0x000000021a4940> (NoMethodError) 'क्यों मिल रही है? – flyer

0

मैं कुछ समय पहले इस के लिए एक मणि लिखा था। इसे "फ्लेक्सिबल" कहा जाता है और rubygems के माध्यम से उपलब्ध नहीं है, लेकिन कल तक जिथब के माध्यम से उपलब्ध था। मैंने इसे हटा दिया क्योंकि यह मेरे लिए बेकार था।

आप किसी भी त्रुटि मिल रही है बिना

class Foo 
    include Flexible 
end 
f = Foo.new 
f.bar = 1 
इसके साथ

कर सकते हैं। तो आप फ्लाई पर किसी ऑब्जेक्ट से इंस्टेंस चर सेट कर सकते हैं और प्राप्त कर सकते हैं। यदि आप interessted हैं ... मैं फिर से github करने के लिए स्रोत कोड अपलोड कर सकता था। यह वस्तु पूछ अगर एक उदाहरण चर "बार" या परिभाषित किया गया है नहीं, लेकिन कुछ और चल रहा है के लिए

f.bar? 
#=> true 
पद्धति के रूप में

सक्षम करने के लिए कुछ संशोधन की जरूरत है।

सधन्यवाद, musicmatze

+0

यह बहुत खतरनाक लगता है। कम से कम, आप टाइप करने योग्य कोड में टाइपो बदल जाते हैं। मैं ऐसी स्थिति के बारे में नहीं सोच सकता जहां यह आवश्यक होगा, अकेले ही एक अच्छा विचार बनें। – Huliax

0

यह पिछले जवाब के सभी तरह लग रहा है मान लेते हैं आप जानते हैं कि क्या वर्ग है कि आप tweak चाहते हैं उसका नाम है जब आप अपने कोड लिख रहे हैं है। खैर, यह हमेशा सत्य नहीं है (कम से कम, मेरे लिए नहीं)। मैं कक्षाओं के ढेर पर फिर से चल रहा हूं कि मैं कुछ परिवर्तनीय (कहना, कुछ मेटाडेटा या कुछ पकड़ना) देना चाहता हूं। उस मामले में ऐसा कुछ काम करेगा,

# example classes that we want to tweak 
class Foo;end 
class Bar;end 
klasses = [Foo, Bar] 

# iterating over a collection of klasses 
klasses.each do |klass| 
    # #class_eval gets it done 
    klass.class_eval do 
    attr_accessor :baz 
    end 
end 

# it works 
f = Foo.new 
f.baz # => nil 
f.baz = 'it works' # => "it works" 
b = Bar.new 
b.baz # => nil 
b.baz = 'it still works' # => "it still works"