Что такое "главное" в Ruby?
Если я запускаю этот файл как "ruby x.rb
":
class X
end
x = X.new
Что это за вещь, которая зоветX.new
"?
Это объект / процесс / и т. Д.?
6 ответов
Все в Ruby происходит в контексте какого-либо объекта. Объект на верхнем уровне называется "основным". По сути, это экземпляр Object со специальным свойством, которое любые методы, определенные там, добавляются как методы экземпляра Object (поэтому они доступны везде).
Таким образом, мы можем сделать скрипт, состоящий исключительно из:
puts object_id
@a = 'Look, I have instance variables!'
puts @a
и он напечатает "105640" и "Смотри, у меня есть переменные экземпляра!".
Это не то, что вам обычно нужно беспокоиться, но оно есть.
Вызывающий объект верхнего уровня - это объект main, который относится к классу Object.
Попробуйте эту программу ruby:
p self
p self.class
Это Х класс. Вы вызываете метод "new", который создает объект класса X. Итак, если вы запустите этот текст как скрипт, Ruby:
- создает новый класс X, который является подклассом Object, и который автоматически (как подкласс Object) наследует некоторые методы, из которых
new
это один. - устанавливает имя
x
- вызывает
new
метод на этом новом классеX
создание объекта экземпляра X; х получает ссылку на этот объект.
Это рубиновый переводчик
x = X.new
Как и во многих языках сценариев, сценарий интерпретируется сверху вниз, а не имеет стандартного метода точки входа, как большинство компилируемых языков.
объект, в контексте которого выполняется код верхнего уровня. Это означает, что на верхнем уровне ссылается на объект:
$ ruby -e 'p self'
main
И этоruby
следует за цепочкой поиска методов, чтобы определить, какой метод вызывать:
$ ruby -e 'p singleton_class.ancestors'
[#<Class:#<Object:0x00007f9e9fdee230>>, Object, Kernel, BasicObject]
Может быть больше, но это то, что вы получаете с самого начала.
сам является экземпляром:
$ ruby -e 'p self.class'
Object
Он имеет одноэлементный класс с двумя методами (метод и псевдоним, если быть более точным):
$ ruby -e 'p singleton_class.instance_methods(false)'
[:inspect, :to_s]
$ ruby -e 'p singleton_methods'
[:inspect, :to_s]
Это определено здесь.
Как видите егоto_s
метод возвращает"main"
(переопределяет поведение ), что вы получаете, когда делаетеp self
.
Вы можете подумать, что код, который вы выполняете, помещается в метод , после чего метод вызывается. По линиям:
main = Object.new
class Object
def main.go
<your code here>
end
end
main.go
Это грубая идея. Позвольте мне обосновать это в нескольких шагах.
В Ruby вы можете на самом деле вкладывать методы, но каждый раз, когда вы вызываете внешний метод, внутренний определяется/переопределяется. Что еще более важно, он определен как метод экземпляра окружающего класса:
class A
def m
def m2; end
end
end
A.new.m
p A.instance_methods(false) # [:m2, :m]
То же самое происходит и здесь, но объемлющий класс в этом случае является одноэлементным классом :
class A
class << self
def m
def m2; end
end
end
end
A.m
p A.singleton_class.instance_methods(false) # [:m2, :m]
А что, если мы воспользуемсяdef self.<name>
обозначение?
class A
def self.m
def m2; end
end
end
A.m
p A.singleton_class.instance_methods(false) # [:m]
p A.instance_methods(false) # [:m2]
Так,self.
влияет толькоm
,m2
становится методом экземпляраA
.
На самом деле вместо может быть какой-то случайный объект:
o = Object.new
A = Class.new do
def o.m
def m2; end
end
end
o.m
p o.singleton_class.instance_methods(false) # [:m]
p A.instance_methods(false) # [:m2]
я должен был использоватьClass.new
потому что сclass
o
не будет виден внутри определения класса.
Или на самом деле у меня не было:
class A
o = Object.new
def o.m
def m2; end
end
o.m
p o.singleton_class.instance_methods(false) # [:m]
p A.instance_methods(false) # [:m2]
end
Но давайте проигнорируем эту ветвь мысли.
Пара изменений, и вы получите это:
main = Object.new
Object.class_eval do
def main.go
@a = 1
def m2
puts @a
end
m2 # 1
end
end
main.go
p Object.instance_methods(false) # [:m2]
p main.instance_variables # [:@a]
я должен был использоватьclass_eval
чтобы он не жаловался, что я пытаюсь переопределить константу.
Вы также можете добавить:
def main.to_s
"main"
end
main.instance_eval { alias inspect to_s }
для полноты.
Другой способ — использовать глобальные переменные:
$main = Object.new
class Object
def $main.go
@a = 1
def m2
puts @a
end
m2 # 1
end
end
$main.go
p Object.instance_methods(false) # [:m2]
p $main.instance_variables # [:@a]
Конечно переменные /$main
иgo
метод не существует. Но больше никаких недостатков не приходит на ум, когда я думаю об этой идее. Идея в том, что это работает так, как если бы ваш код помещался в метод и выполнялся при запуске метода.
Также это объясняет, почему методы, определенные на верхнем уровне, видны везде:
a.rb
:
f
$ ruby -e 'def f; puts "f"; end; require "./a"'
f
Поскольку они становятся методами экземпляра .
И вы можете использовать переменные экземпляра, которые являются переменными экземпляра объекта.
UPD Я заметил, что нельзя определять константы (как обычно), классы и модули вmain.go
. Таким образом, абстракция кажется негерметичной. Я мог бы попытаться изменить его:
Object.class_eval do
<your constants, classes, modules, methods>
def main.go
<the rest of the code>
end
end
Но на данный момент я бы скорее сказал, что на высшем уровнеself
указывает наmain
объект, а текущая ссылка класса наObject
сорт. Подробнее о ссылках на классы здесь.
Как сказал Чарли Мартин, X.new - это вызов конструктора класса X, который возвращает объект типа X, хранящийся в переменной x.
Исходя из вашего названия, я думаю, что вы ищете немного больше. Ruby не нуждается в main, он выполняет код в том порядке, в котором он его видит. Таким образом, зависимости должны быть включены до их вызова.
Таким образом, ваша главная задача - это любой код в процедурном стиле, который написан вне определения класса или модуля.