Аз идвам от Python фон, и се опитвах да разбера как Ruby включва сравнение с множественото наследяване на Python. super
работа.
Аз включих Mod
, тогава Mod2
, тогава Mod
отново. Въпреки това, A
все още изглежда, че се прави позоваване Mod2
.
module Mod
def foo(x)
x**2
end
end
module Mod2
def foo(x)
x*2
end
end
class A
include Mod
def foo(x)
super(x) + 1
end
end
A.new.foo(5) == 26 # true
class A
include Mod2
def foo(x)
super(x) + 1
end
end
A.new.foo(5) == 11 # true
class A
include Mod
def foo(x)
super(x) + 1
end
end
A.new.foo(5) == 26 # false. Is 11
Защо не е третата A.new.foo(5)
връщане 26
?
Отговори:
1 за отговор № 1Мисля, че това, което може да ви липсва, е второто class A ... end
не предефинира класа, той го добавя. Позволено е да "отваряте" класа многократно в рубин и да (определяте) каквото искате. Това, и това, което изглежда, вече сте измислили, което е това include
повече или по-малко просто изтрива кода от модула в това място в дефиницията на класа, освен ако този модул вече е бил включен, в който случай не прави нищо.
Можете да направите това с всеки клас (независимо дали е препоръчително или не)
class Hash
def to_a
"lol you probably wanted an array here"
end
end
{foo: :bar}.to_a # Returns above string instead of [:foo, :bar]
1 за отговор № 2
Определяне class A(object)
в PythonCreates обект с a type
на type
и __name__
на A
, след това възлага този обект на етикета A
, Секунда class A(object)
Дефиницията създава съвсем ново type
предмет, който след това се поставя в етикет А.
Класове в рубин не се съхраняват в етикети и могат да се отварят, за да се добавят повече функционалности. Добър пример за това е 2.days
, Rails определя days
на цяло число. Без релси, 2.days
неуспешна.
По-горе дефинираните 3 класа са приблизително еквивалентни на:
class A
include Mod
def foo(x)
super(x) + 1
end
include Mod2
def foo(x)
super(x) + 1
end
include Mod
def foo(x)
super(x) + 1
end
end
Когато руби вижда второто include Mod
, знае Mod
вече е включен в класа и го прескача, напускайки foo
от Mod2
последната дефиниция.
Ако премахнете foo
от по-късни определения, A
все още има достъп до нея:
class A
include Mod
def foo(x)
super(x) + 1
end
end
A.new.foo(5) == 26 # true
class A
include Mod2
end
A.new.foo(5) == 11 # true
class A
include Mod
end
A.new.foo(5) == 11
0 за отговор № 3
Ruby намира super
чрез търсене в предшествената верига на клас / модуледин клас / модул в даден момент, докато намери дефиницията на метода, който търси. Като автобус с много спирки, търсейки правилното място за слизане, но винаги се изключва при първата възможност. става докрай BasicObject
и няма да го спре, че ще вдигне NoMethodError
.
Когато включите модул, вие го добавяте към веригата на предците точно над включения клас / модул. Module.ancestors
Можете да включите същия модул многократно, но той ще бъде добавен само към веригата на предците. Помислете за това:
module Mod
def foo(x)
x**2
end
end
module Mod2
def foo(x)
x*2
end
end
class A
include Mod
def foo(x)
super(x) + 1
end
end
A.ancestors # => [A, Mod, Object, Kernel, BasicObject]
class A
include Mod2
def foo(x)
super(x) + 1
end
end
A.ancestors # => [A, Mod2, Mod, Object, Kernel, BasicObject]
class A
include Mod
def foo(x)
super(x) + 1
end
end
A.ancestors # => [A, Mod2, Mod, Object, Kernel, BasicObject]
Mod
вече беше във веригата на родословието втори път, когато беше включен, така че нищо ново не се случи. Той остава сложен, и super
все още удари Mod2
на първо място. Накратко, не можете да "замените" по-новите модули, като включите по-стари модули, които вече са били там. Има и други неща, които можете да направите. Mod3
, или може би ModOverride
, със същото определение за foo
както беше в Mod
и включете това или разгледайте уточненията Ruby 2.0 Как да се изключи модул от модул след неговото включване?
Странична бележка
Както отбелязват други отговори, не е необходимо да предефинирате A#foo
всеки път, когато определението остава същото.