12. Интроспекция и метапрограмиране, част 3

12. Интроспекция и метапрограмиране, част 3

12. Интроспекция и метапрограмиране, част 3

18 ноември 2015

Днес

Първи тест

Идната седмица...

Въпрос 1

Кажете ми всичко, което знаете за instance променливите.

  • Пазят се в обект
  • Достъпни са в наследници
  • Не са директно достъпни извън обекта
  • Трябва да се ползват методи за достъп (напр. attr_accessor)
  • Могат да се достъпят "заобиколно" с instance_variable_get и компания

Въпрос 2

Кой е класът на {}? На Integer? На Class?

Кой е родителят на String? На Object? На Class?

{}.class == Hash       # => true
Integer.class == Class # => true
Class.class == Class   # => true

String < Object        # => true
Object < BasicObject   # => true
Class < Module         # => true

Въпрос 3

Какво прави instance_eval? Ами instance_exec?

Изпълнява блока с променен self. instance_exec е същото, но може да подава аргументи на блока си.

Въпрос 4

Как Ruby знае къде да постави метод, дефиниран с def?

Винаги има текущ клас, в който този метод отива. Той може да се промени с module, class и class_eval. Всъщност, дори с instance_eval, но за това — по-късно.

Въпрос 5

Можем ли да направим така, че вместо изключения, несъществуващи локални променливи или методи да резултират в nil? Как?

foo          # => nil
foo.bar.baz  # => nil

Можем, като предефинираме метода method_missing в BasicObject.

class BasicObject
  def method_missing(*) end
end

define_method

припомняне

Ако имаме името на метод в променлива, например method_name = 'first_name', бихме могли да ползваме define_method, за да създадем динамично метод:

class MyObject
  method_name    = 'first_name'
  implementation = -> { @data_store['first_name'] }

  define_method(method_name, &implementation)
end

define_method

някои алтернативи

method_name    = 'first_name'
implementation = -> { @data_store['first_name'] }

MyObject.send :define_method, method_name, &implementation

Или:

some_class     = MyObject
method_name    = 'first_name'
implementation = -> { @data_store['first_name'] }

some_class.class_eval do
  define_method(method_name, &implementation)
end

Най-важните неща от миналата лекция

instance_eval

some_list = []

some_list.instance_eval do
  push 5
  push 'foo'
  push :bar
  push [:another, :list]

  size # => 4
end

some_list # => [5, "foo", :bar, [:another, :list]]

def object.method

Може да (пре)дефинирате метод в конкретен обект.

things = [22, :f, 'Sofia']

def things.size
  -5
end

def things.asl
  "#{self[0]}/#{self[1]}/#{self[2]}"
end

things        # => [22, :f, "Sofia"]
things.size   # => -5
things.length # => 3
things.asl    # => "22/f/Sofia"

[].asl        # => error: NoMethodError
[].size       # => 0

Singleton класове

обяснението на предишния слайд

Singleton класове

визуализация

Object#singleton_class

Собственият клас е достъпен чрез #singleton_class

things = []

def things.answer
  42
end

things.singleton_class
# => #<Class:#<Array:0x002b572f8bc548>>
things.singleton_class.instance_methods(false)
# => [:answer]
[].singleton_class.instance_methods(false)
# => []

Symbol и Integer

...и техните singleton класове

Целите числа и символите нямат собствени класове. Това е заради специалното им вътрешно представяне в Ruby.

1_000.singleton_class # => error: TypeError
:blah.singleton_class # => error: TypeError

class << thing

алтернативен синтаксис

Можете да отворите собствения клас на обект с class <<

things = [22, :f, 'Sofia']

class << things
  def size
    -5
  end

  def asl
    "#{self[0]}/#{self[1]}/#{self[2]}"
  end
end

things.asl  # => "22/f/Sofia"
things.size # => -5

super и singleton класове

Оригиналният метод е достъпен чрез super.

super и singleton класове (2)

things = [22, :f, 'Sofia']

class << things
  def each
    super
    yield :P
  end
end

aggregated = []
things.each do |thing|
  aggregated << thing
end

aggregated # => [22, :f, "Sofia", :P]

super и singleton класове

OMG момент

Някой има ли идея защо super работи?

things = []

def things.answer
  42
end

things.singleton_class # => #<Class:#<Array:0x002b91ce2dc478>>
things.singleton_class.superclass # => Array

Да, singleton класът е наследник на класа на обекта.

superclass и singleton класове

визуализация

Въпроси дотук?

Ще продължим с преглед отвътре на "класовите" методи.

Класови методи

Вероятно помните, че класови методи могат да се дефинират така:

class Something
  def Something.foo() 42 end
  def self.bar() 42 end

  class << self
    def qux() 42 end
  end
end

Класови методи

...всъщност

Класовите методи се пазят в собствения клас на класа

class Something
  def self.answer() 42 end
end

Something.singleton_class # => #<Class:Something>
Something.singleton_class.instance_methods(false) # => [:answer]

Класови методи

визуализация

Класови методи

instance_eval върху класове

class Something; end

Something.instance_eval do
  def say_something() 'something' end
end

Something.say_something # => "something"
Something.new.say_something # => error: NoMethodError

Класови методи

class_eval

class Something; end

Something.class_eval do
  def say_something() 'something' end
end

Something.say_something # => error: NoMethodError
Something.new.say_something # => "something"

extend

...върху клас

Помните ли extend?

module Knowledge
  def answer() 42 end
end

class Something
  extend Knowledge
end

Something.answer # => 42

extend

...върху не-клас

module Knowledge
  def answer() 42 end
end

text = "fourty-two"
text.extend Knowledge

text.answer # => 42

Сещате ли се как може да се имплементира?

extend

...с class <<

module Knowledge
  def answer() 42 end
end

class Something; end

class << Something
  include Knowledge
end

Something.answer # => 42

extend

...чрез instance_eval и singleton клас

module Knowledge
  def answer() 42 end
end

class Something; end

Something.singleton_class.instance_eval { include Knowledge }

Something.answer # => 42

extend

...чрез include

module Knowledge
  def answer() 42 end
end

class Something; end

Something.singleton_class.include Knowledge

Something.answer # => 42

Изводи:

Въпроси дотук?

Ще продължим да дълбаем в класовите методи при наследяване.

Класови методи и наследяване

Класовите методи на родителя са достъпни в класовите методи на наследника:

class Something
  def self.answer() 42 end
end

class Other < Something
  def self.better_answer() answer * 2 end
end

Other.better_answer # => 84

Класови методи и наследяване

друг OMG момент

Собственият клас на наследника наследява собствения клас на родителя:

class Something; end
class Other < Something; end

Something.singleton_class        # => #<Class:Something>
Other.singleton_class.superclass # => #<Class:Something>

Something.singleton_class == Other.singleton_class.superclass # => true

Класови методи и наследяване

визуализация

Класови методи и наследяване

takeaway

Singleton класът на суперкласа е суперкласът на singleton класа.

Класови методи и наследяване

Класови методи и наследяване

ancestors

class Foo
end

Foo.singleton_class.ancestors
# => [#<Class:Foo>,
#     #<Class:Object>,
#     #<Class:BasicObject>,
#     Class,
#     Module,
#     Object,
#     PP::ObjectMixin,
#     Kernel,
#     BasicObject]

Ancestor chains

Няколко примера за ancestor chains

Относно търсенето на (инстанционни) методи на обектите от тип String:

''.singleton_class.ancestors
# => [#<Class:#<String:0x007f8788e51bd8>>,
#     String, Comparable, Object, Kernel, BasicObject]

''.class.ancestors
# => [String, Comparable, Object, Kernel, BasicObject]

String.ancestors
# => [String, Comparable, Object, Kernel, BasicObject]

Ancestor chains

за "класови" методи

По отношение на "класовите" методи, викани върхy String:

String.singleton_class.ancestors
# => [#<Class:String>, #<Class:Object>, #<Class:BasicObject>,
#     Class, Module, Object, Kernel, BasicObject]

String.class.ancestors
# => [Class, Module, Object, Kernel, BasicObject]

Class.ancestors
# => [Class, Module, Object, Kernel, BasicObject]

Grand Unified Theory

  1. Има само един вид обекти - били те обикновени или модули
  2. Има само един вид модули - били те обикновени или клас
  3. Има само един вид методи - живеят в модули, които често са класове
  4. Всеки обект има "реален клас" - бил той обикновен клас или собствен клас
  5. Всеки клас има точно един суперклас - с изключение на BasicObject
  6. Суперкласът на singleton класа на обект е класът на обекта. Суперкласът на singleton класа на клас е singleton класът на родителя на класа.
  7. При извикване на метод, Ruby взема "реалния клас" и търси в неговия ancestor chain

Въпроси