Ruby: виклик методу класу з екземпляра


347

Як ви називаєте метод класу в Ruby з одного з екземплярів цього класу? Скажи, що я

class Truck
  def self.default_make
    # Class method.
    "mac"
  end

  def initialize
    # Instance method.
    Truck.default_make  # gets the default via the class's method.
    # But: I wish to avoid mentioning Truck. Seems I'm repeating myself.
  end
end

рядок Truck.default_makeвилучає за замовчуванням. Але чи є спосіб сказати це без згадки Truck? Здається, що має бути.

Відповіді:


563

Замість того, щоб посилатися на буквальне ім'я класу, всередині методу екземпляра можна просто зателефонувати self.class.whatever.

class Foo
    def self.some_class_method
        puts self
    end

    def some_instance_method
        self.class.some_class_method
    end
end

print "Class method: "
Foo.some_class_method

print "Instance method: "
Foo.new.some_instance_method

Виходи:

Метод класу: Foo
Метод екземпляра: Foo

7
Я хотів би побачити ярлик у рубіні, щоб викликати метод класу з екземпляра. тобто:> some_class_method замість self.class.some_class_method
phoet

7
хоча це правильна відповідь, прикро, що "self.class" є більш типізованим та менш простим для читання, ніж назва класу "Truck". ой добре ....
Метт Конноллі,

22
@MattConnolly, це відносно, якщо назва вашого класу - SalesforceSyncJobце коротше;)
Древній

29
@MattConnolly, також за допомогою self.classвиключає необхідність пошуку / заміни, якщо ви перейменовуєте клас.
Gus Shortz

8
@GusShortz правда. Крім того, self.class працює краще, якщо є підклас.
Метт Конноллі,

183

Використання self.class.blahНЕ таке, як використання, ClassName.blahколи мова йде про спадкування.

class Truck
  def self.default_make
    "mac"
  end

  def make1
    self.class.default_make
  end

  def make2
    Truck.default_make
  end
end


class BigTruck < Truck
  def self.default_make
    "bigmac"
  end
end

ruby-1.9.3-p0 :021 > b=BigTruck.new
 => #<BigTruck:0x0000000307f348> 
ruby-1.9.3-p0 :022 > b.make1
 => "bigmac" 
ruby-1.9.3-p0 :023 > b.make2
 => "mac" 

58
Здається, це відповідь на прийняту відповідь, а не відповідь на питання.
zhon

16
@zohn - правда, але це все-таки корисний контекст при розгляді того, що використовувати.
Метт Сандерс

1
@MattSanders просто використовує коментар у цих випадках.
nandilugio

1
@hlcs self.classє правильним для збереження спадщини. незважаючи на те make1(), що визначено в Truck, він посилається BigTruckна метод класу.
Кайзер Шахід

14

Щоб отримати доступ до методу класу всередині методу екземпляра, виконайте наступне:

self.class.default_make

Ось альтернативне рішення вашої проблеми:

class Truck

  attr_accessor :make, :year

  def self.default_make
    "Toyota"
  end

  def make
    @make || self.class.default_make
  end

  def initialize(make=nil, year=nil)
    self.year, self.make = year, make
  end
end

Тепер давайте використовувати наш клас:

t = Truck.new("Honda", 2000)
t.make
# => "Honda"
t.year
# => "2000"

t = Truck.new
t.make
# => "Toyota"
t.year
# => nil

make не повинен бути екземплярним методом. це скоріше фабрика, яка повинна бути прив’язана до класу, а не до екземпляра
phoet

6
@phoet Слово make позначає марку автомобіля (як у Toyota, BMW тощо) englishforums.com/English/AMakeOfCar/crcjb/post.htm . Номенклатура базується на вимозі користувача
Harish Shetty

8

Якщо у вас є доступ до методу делегата, ви можете зробити це:

[20] pry(main)> class Foo
[20] pry(main)*   def self.bar
[20] pry(main)*     "foo bar"
[20] pry(main)*   end  
[20] pry(main)*   delegate :bar, to: 'self.class'
[20] pry(main)* end  
=> [:bar]
[21] pry(main)> Foo.new.bar
=> "foo bar"
[22] pry(main)> Foo.bar
=> "foo bar"

Крім того, і, ймовірно, чистіше, якщо у вас є більше методу або двох, які ви хочете делегувати класу і екземпляру:

[1] pry(main)> class Foo
[1] pry(main)*   module AvailableToClassAndInstance
[1] pry(main)*     def bar
[1] pry(main)*       "foo bar"
[1] pry(main)*     end  
[1] pry(main)*   end  
[1] pry(main)*   include AvailableToClassAndInstance
[1] pry(main)*   extend AvailableToClassAndInstance
[1] pry(main)* end  
=> Foo
[2] pry(main)> Foo.new.bar
=> "foo bar"
[3] pry(main)> Foo.bar
=> "foo bar"

Слово обережності:

Не просто випадковим чином delegateвсе, що не змінює стан до класу та екземпляра, тому що ви почнете стикатися з проблемами зіткнення дивних імен. Робіть це скромно і лише після того, як ви перевірили, нічого іншого не збито.



5

Ви робите це правильно. Методи класу (подібні до «статичних» методів у C ++ або Java) не є частиною екземпляра, тому на них потрібно посилатися безпосередньо.

Зверніть увагу, що у вашому прикладі вам краще послужити, роблячи "default_make" звичайним методом:

#!/usr/bin/ruby

class Truck
    def default_make
        # Class method.
        "mac"
    end

    def initialize
        # Instance method.
        puts default_make  # gets the default via the class's method.
    end
end

myTruck = Truck.new()

Методи класу більш корисні для функцій типу утиліти, які використовують клас. Наприклад:

#!/usr/bin/ruby

class Truck
    attr_accessor :make

    def default_make
        # Class method.
        "mac"
    end

    def self.buildTrucks(make, count)
        truckArray = []

        (1..count).each do
            truckArray << Truck.new(make)
        end

        return truckArray
    end

    def initialize(make = nil)
        if( make == nil )
            @make = default_make()
        else
            @make = make
        end
    end
end

myTrucks = Truck.buildTrucks("Yotota", 4)

myTrucks.each do |truck|
    puts truck.make
end

2
Я не згоден, що це default_makeповинен бути метод екземпляра. Навіть якщо це простіше для цих прикладів, це не правильна семантика - за замовчуванням це добуток класу, а не об'єкти, які належать до класу.
Пітер

1
@Peter Ви б хотіли пояснити це простішими словами? Я просто вивчаю відповіді Рубі і Махи, мені здається ідеальними.
Marlen TB

1
@ MarlenT.B. озираючись назад, я не впевнений, що тут можна багато чого навчитися - я тільки сперечався про те, де найкраще місце для методу, і я вже не купую власний аргумент так сильно! :)
Петро

2
Я також не згоден. Будь-який метод класу не має нічого спільного з "корисністю". Йдеться про те, чи метод концептуально застосовується до класу, чи до об'єкта цього класу. Наприклад, кожен вантажівка має різний серійний номер, тому serial_number - це метод примірника (з відповідною змінною екземпляра). На іншому транспортному засобі (який повертає "вантажівку") слід застосовувати класний метод, оскільки це властивість всіх вантажних автомобілів, а не конкретної вантажівки
vish

3

Ще один:

class Truck
  def self.default_make
    "mac"
  end

  attr_reader :make

  private define_method :default_make, &method(:default_make)

  def initialize(make = default_make)
    @make = make
  end
end

puts Truck.new.make # => mac

1

Ось підхід щодо того, як ви можете реалізувати _classметод, який працює в self.classцій ситуації. Примітка: не використовуйте це у виробничому коді, це заради інтересів :)

З: Чи можете ви оцінювати код у контексті абонента в Ruby? а також http://rubychallenger.blogspot.com.au/2011/07/caller-binding.html

# Rabid monkey-patch for Object
require 'continuation' if RUBY_VERSION >= '1.9.0'
class Object
  def __; eval 'self.class', caller_binding; end
  alias :_class :__
  def caller_binding
    cc = nil; count = 0
    set_trace_func lambda { |event, file, lineno, id, binding, klass|
      if count == 2
        set_trace_func nil
        cc.call binding
      elsif event == "return"
        count += 1
      end
    }
    return callcc { |cont| cc = cont }
  end
end

# Now we have awesome
def Tiger
  def roar
    # self.class.roar
    __.roar
    # or, even
    _class.roar
  end
  def self.roar
    # TODO: tigerness
  end
end

Можливо, правильна відповідь - подати патч для Ruby :)


-6

Подібним запитанням ви можете скористатися:

class Truck
  def default_make
    # Do something
  end

  def initialize
    super
    self.default_make
  end
end
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.