Что такое Ruby-эквивалент классов данных Python?

Какой аналог класса данных Python существует в Ruby?

В Python можно использовать классы данных для краткого определения класса без явного определения для него метода инициализатора/__init__.

Без классов данных в Python это выглядело бы так (включая ввод аргументов):

class myNewDog2:
    def __init__(fur_colour: str, weight: float, is_microchipped: bool, is_asleep:bool=False):      
      self.fur_colour = fur_colour
      self.weight = weight
      self.is_microchipped = is_microchipped
      self.is_asleep = is_asleep
   
my_dog = myDog("tri-colour", 19.52, True)

Но его можно свести к форме, не требующей двойной записи аргументов, используя классы данных:

from dataclasses import dataclass

@dataclass
class myDog2:
    fur_colour: str
    weight: float
    is_microchipped: bool
    is_asleep: bool = False # Optional arg / defaults to False
   
my_dog = myDog("tri-colour", 19.52, True)

Вот как выглядит эквивалентное определение класса в Ruby:

class MyDog
  def initialize(fur_colour:, weight:, is_microchipped:, is_asleep: false)
    @fur_colour = fur_colour
    @weight = weight
    @is_microchipped = is_microchipped
    @is_asleep = is_asleep
  end 
end

Как бы я определил этот класс в Ruby так же кратко, как и в Python? Можно ли сказать Ruby заполнить пробелы в конструкторе, основываясь только на определении атрибутов, которые я хочу, чтобы класс принимал и поддерживал?

Думаю, вы ищете класс данных Ruby: docs.ruby-lang.org/en/3.2/Data.html

spickermann 22.05.2024 15:31

Ха-ха, буквально class Data — да, это то, что я искал, но, кажется, пропустил. Спасибо

MolarFox 22.05.2024 16:26

в чем разница между Ruby Data и Struct?

Lam Phan 23.05.2024 10:51
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
3
3
161
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Аналогичным способом в Ruby является класс данных , который позволяет вам определять простой объект значения следующим образом:

MyDog = Data.define(:fur_colour, :weight, :is_microchipped, :is_asleep)

И вы можете создавать экземпляры с позиционными или ключевыми аргументами, например:

my_dog = MyDog.new('tri-colour', 19.52, true, false) # or
my_dog = MyDog.new(fur_colour: 'tri-colour', weight: 19.52, is_microchipped: true, is_asleep: false)

Обратите внимание, что класс Data пока не поддерживает необязательные аргументы, такие как аргумент is_asleep в вашем примере (проверено на Ruby 3.3.1). Вам всегда необходимо предоставлять такое же количество аргументов, как определено.

Общая проблема

Что такое Ruby-эквивалент классов данных Python?
Какой аналог класса данных Python существует в Ruby?

Такого не существует.

Это справедливо не только для классов данных и не только для Python и Ruby. Это справедливо практически для любой функции X и пары языков A и B.

Каждый язык уникален, поэтому почти всегда будут тонкие различия, и почти никогда не будет полного эквивалента или аналога в другом языке.

Вместо того, чтобы спрашивать, что является эквивалентом функции X, вам следует объяснить, в чем заключается реальная основная проблема, которую вы пытаетесь решить, и спросить, как решить эту проблему на языке B.

Это похоже на Проблема XY: вы пытаетесь решить проблему X. Вы думаете, что способ решить проблему X — это решение Y. Вы столкнулись с проблемой Y. Вы просите о помощи с Y. Люди пытаются помочь вы, но очень сбиты с толку, потому что Y вообще кажется странной проблемой. После многих неудачных попыток и большого количества потерянного времени вы, наконец, рассказываете им о проблеме X, и оказывается, что существует действительно простое решение X, и даже если бы вы могли заставить Y работать, это не решило бы проблему. проблема в любом случае.

Показательный пример: @dataclasses.dataclass — это декоратор. В Ruby нет декораторов. Итак, уже тут мы сталкиваемся с проблемой, что эквивалента просто не может быть.

Итак, забудем о декораторе. Декораторы классов Python — это просто синтаксический сахар для функции, которая принимает класс в качестве аргумента и возвращает класс. Но здесь мы сталкиваемся со следующей проблемой: в Ruby нет функций.

Хорошо, а что насчет методов? Конечно, у нас может быть метод в Ruby, который принимает класс в качестве аргумента и возвращает класс. И действительно, мы можем. Но декоратор Python @dataclasses.dataclass перебирает все атрибуты класса и генерирует различные функции на основе этих атрибутов. Ближайшим эквивалентом атрибутов Python являются переменные экземпляра Ruby, но вот последний гвоздь в гроб: переменные экземпляра вообще не объявляются в Ruby, они просто волшебным образом возникают.

Таким образом, методу просто нечего перебирать. Реализация чего-то вроде декоратора @dataclasses.dataclass в Ruby принципиально невозможна.

Еще одна вещь, которую делает декоратор @dataclasses.dataclass, — это генерирует понятное строковое представление, включающее имя класса. Классы в Ruby не имеют имени, так что это тоже невозможно.

Сверните свой собственный

Но в Ruby можно делать и другие вещи. Хотя невозможно перебирать переменные экземпляра, заданные только классом (просто потому, что переменные экземпляра являются свойством экземпляра, а не класса), безусловно, можно сгенерировать класс, зная список имен переменных экземпляра. Что-то вроде этого:

module Kernel
  private

  def data_class(*instance_variables, superclass: Object)
    Class.new(superclass) do
      instance_variables.each(&method(:attr_accessor))

      define_method(:initialize) do |*args|
        instance_variables.zip(args).each do |instance_variable, arg|
          send("#{instance_variable} = ", arg)
        end
      end

      define_method(:to_s) do
        class_name = self.class.name
        ivar_values = instance_variables.map do |instance_variable|
          "#{instance_variable}: #{send(instance_variable)}"
        end.join(', ')

        "#{class_name}(#{ivar_values})"
      end

      alias_method :inspect, :to_s
    end
  end
end

И вы можете использовать его следующим образом:

MyDog = data_class(:fur_colour, :weight, :is_microchipped, :is_asleep)

my_dog = MyDog.new('tri-colour', 19.52, true, false)

Очевидно, что это оставляет желать лучшего с точки зрения удобства использования и гибкости, но это подтверждает концепцию, и не должно быть слишком сложно расширить ее, чтобы сделать ее более безопасной, более удобной для пользователя и т. д.

Struct

Базовая библиотека Ruby содержит две реализации схожей идеи.

Первый — это класс Struct. Как сказано в документации:

Класс Struct предоставляет удобный способ создания простого класса, который может хранить и извлекать значения.

MyDog = Struct.new(:fur_colour, :weight, :is_microchipped, :is_asleep)

my_dog = MyDog.new('tri-colour', 19.52, true, false)

Data

Второй — Класс данных:

Класс Data предоставляет удобный способ определения простых классов для объектов со схожими значениями.

MyDog = Data.define(:fur_colour, :weight, :is_microchipped, :is_asleep)

my_dog = MyDog.new('tri-colour', 19.52, true, false)

Struct против Data

Наличие двух разных решений сразу же вызывает вопрос: так в чем же разница?

Я вижу два основных различия:

Другие вопросы по теме