ruby 什么时候应该使用Struct vs. OpenStruct?

fivyi3re  于 2023-05-17  发布在  Ruby
关注(0)|答案(9)|浏览(98)

一般来说,与Struct相比,使用OpenStruct的优点和缺点是什么?哪种类型的通用用例适用于每种情况?

g52tjvyc

g52tjvyc1#

使用OpenStruct,您可以任意创建属性。另一方面,Struct必须在创建时定义其属性。选择哪一个主要取决于您是否需要在以后添加属性。
考虑它们的方法是将其作为一方的哈希和另一方的类之间的中间地带。与Hash相比,它们暗示了数据之间更具体的关系,但它们没有类那样的示例方法。例如,一个函数的一堆选项在散列中是有意义的;他们只是松散的联系。函数所需的姓名、电子邮件和电话号码可以打包在StructOpenStruct中。如果姓名、电子邮件和电话号码需要方法以“First Last”和“Last,First”格式提供姓名,那么您应该创建一个类来处理它。

oxalkeyp

oxalkeyp2#

其他基准:

require 'benchmark'
require 'ostruct'

REP = 100000

User = Struct.new(:name, :age)

USER = "User".freeze
AGE = 21
HASH = {:name => USER, :age => AGE}.freeze

Benchmark.bm 20 do |x|
  x.report 'OpenStruct slow' do
    REP.times do |index|
       OpenStruct.new(:name => "User", :age => 21)
    end
  end

  x.report 'OpenStruct fast' do
    REP.times do |index|
       OpenStruct.new(HASH)
    end
  end

  x.report 'Struct slow' do
    REP.times do |index|
       User.new("User", 21)
    end
  end

  x.report 'Struct fast' do
    REP.times do |index|
       User.new(USER, AGE)
    end
  end
end

对于那些不耐烦的人来说,他们想了解基准测试结果,而不需要自己运行它们,这里是上面代码的输出(在MB Pro 2.4GHz i7上)

user     system      total        real
OpenStruct slow       4.430000   0.250000   4.680000 (  4.683851)
OpenStruct fast       4.380000   0.270000   4.650000 (  4.649809)
Struct slow           0.090000   0.000000   0.090000 (  0.094136)
Struct fast           0.080000   0.000000   0.080000 (  0.078940)
k5hmc34c

k5hmc34c3#

更新:

创建100万示例的计时:

0.357788 seconds elapsed for Class.new (Ruby 2.5.5)
0.764953 seconds elapsed for Struct (Ruby 2.5.5)
0.842782 seconds elapsed for Hash (Ruby 2.5.5)
2.211959 seconds elapsed for OpenStruct (Ruby 2.5.5)

0.213175 seconds elapsed for Class.new (Ruby 2.6.3)
0.335341 seconds elapsed for Struct (Ruby 2.6.3)
0.836996 seconds elapsed for Hash (Ruby 2.6.3)
2.070901 seconds elapsed for OpenStruct (Ruby 2.6.3)

0.936016 seconds elapsed for Class.new (Ruby 2.7.2)
0.453067 seconds elapsed for Struct (Ruby 2.7.2)
1.016676 seconds elapsed for Hash (Ruby 2.7.2)
1.482318 seconds elapsed for OpenStruct (Ruby 2.7.2)

0.421272 seconds elapsed for Class.new (Ruby 3.0.0)
0.322617 seconds elapsed for Struct (Ruby 3.0.0)
0.719928 seconds elapsed for Hash (Ruby 3.0.0)
35.130777 seconds elapsed for OpenStruct (Ruby 3.0.0) (oops!)

0.443975 seconds elapsed for Class.new (Ruby 3.0.1)
0.348031 seconds elapsed for Struct (Ruby 3.0.1)
0.737662 seconds elapsed for Hash (Ruby 3.0.1)
16.264204 seconds elapsed for SmartHash (Ruby 3.0.1)  (meh)
53.396924 seconds elapsed for OpenStruct (Ruby 3.0.1)  (oops!)

请参阅:Ruby 3.0.0 Bug #18032被关闭,因为它是一个功能,而不是一个错误

报价:

  • OpenStruct现在被认为是“一个反模式”,所以我建议你不要再使用OpenStruct。*
  • [Ruby团队]将正确性优先于性能,并回到类似于Ruby 2.2的解决方案 *
    旧答案:

从Ruby 2.4.1开始,OpenStruct和Struct在速度上更加接近。参见https://stackoverflow.com/a/43987844/128421
完整性:结构Hashvs.OpenStruct
在Ruby 1.9.2上运行与burtlo类似的代码(1/4核x86_64,8 GB RAM)[编辑表以对齐列]:

creating 1 Mio Structs :         1.43 sec ,  219 MB /  90MB (virt/res)
creating 1 Mio Class instances : 1.43 sec ,  219 MB /  90MB (virt/res)
creating 1 Mio Hashes  :         4.46 sec ,  493 MB / 364MB (virt/res)
creating 1 Mio OpenStructs :   415.13 sec , 2464 MB / 2.3GB (virt/res) # ~100x slower than Hashes
creating 100K OpenStructs :     10.96 sec ,  369 MB / 242MB (virt/res)

OpenStructs是缓慢内存密集型不适合大型数据集
下面是重现结果的脚本:

require 'ostruct'
require 'smart_hash'

MAX = 1_000_000

class C; 
  attr_accessor :name, :age; 
  def initialize(name, age)
    self.name = name
    self.age = age
  end
end
start = Time.now
collection = (1..MAX).collect do |i|
  C.new('User', 21)
end; 1
stop = Time.now
puts "    #{stop - start} seconds elapsed for Class.new (Ruby #{RUBY_VERSION})"

s = Struct.new(:name, :age)
start = Time.now
collection = (1..MAX).collect do |i|
  s.new('User', 21)
end; 1
stop = Time.now
puts "    #{stop - start} seconds elapsed for Struct (Ruby #{RUBY_VERSION})"

start = Time.now
collection = (1..MAX).collect do |i|
  {:name => "User" , :age => 21}
end; 1
stop = Time.now
puts "    #{stop - start} seconds elapsed for Hash (Ruby #{RUBY_VERSION})"

start = Time.now
collection = (1..MAX).collect do |i|
  s = SmartHash[].merge( {:name => "User" , :age => 21} )
end; 1
stop = Time.now
puts "    #{stop - start} seconds elapsed for SmartHash (Ruby #{RUBY_VERSION})"

start = Time.now
collection = (1..MAX).collect do |i|
  OpenStruct.new(:name => "User" , :age => 21)
end; 1
stop = Time.now
puts "    #{stop - start} seconds elapsed for OpenStruct (Ruby #{RUBY_VERSION})"
ou6hu8tu

ou6hu8tu4#

两者的使用情况完全不同。
你可以把Ruby 1.9中的Struct类看作是C中struct声明的等价物。在Ruby中,Struct.new接受一组字段名作为参数,并返回一个新的Class。类似地,在C中,struct声明接受一组字段,并允许程序员像使用任何内置类型一样使用新的复杂类型。
鲁比:

Newtype = Struct.new(:data1, :data2)
n = Newtype.new

C:

typedef struct {
  int data1;
  char data2;
} newtype;

newtype n;

OpenStruct类可以比作C中的匿名结构声明。它允许程序员创建复杂类型的 * 示例 *。
鲁比:

o = OpenStruct.new(data1: 0, data2: 0) 
o.data1 = 1
o.data2 = 2

C:

struct {
  int data1;
  char data2;
} o;

o.data1 = 1;
o.data2 = 2;

以下是一些常见的用例。
OpenStructs可用于轻松地将哈希转换为响应所有哈希键的一次性对象。

h = { a: 1, b: 2 }
o = OpenStruct.new(h)
o.a = 1
o.b = 2

结构对于速记类定义很有用。

class MyClass < Struct.new(:a,:b,:c)
end

m = MyClass.new
m.a = 1
vfh0ocws

vfh0ocws5#

OpenStructs使用的内存要多得多,而且与Structs相比执行速度较慢。

require 'ostruct' 

collection = (1..100000).collect do |index|
   OpenStruct.new(:name => "User", :age => 21)
end

在我的系统上,下面的代码在14秒内执行,消耗了1.5 GB的内存。您的里程可能会有所不同:

User = Struct.new(:name, :age)

collection = (1..100000).collect do |index|
   User.new("User",21)
end

这几乎是瞬间完成的,消耗了26.6 MB的内存。

8fq7wneg

8fq7wneg6#

Struct

>> s = Struct.new(:a, :b).new(1, 2)
#=> #<struct a=1, b=2>
>> s.a
#=> 1
>> s.b
#=> 2
>> s.c
#=> NoMethodError: undefined method `c` for #<struct a=1, b=2>

OpenStruct

>> require 'ostruct'
#=> true
>> os = OpenStruct.new(a: 1, b: 2)
#=> #<OpenStruct a=1, b=2>
>> os.a
#=> 1
>> os.b
#=> 2
>> os.c
#=> nil
h79rfbju

h79rfbju7#

看一下关于新方法的API。很多差异可以在那里找到。
就我个人而言,我非常喜欢OpenStruct,因为我不必事先定义对象的结构,只需添加我想要的东西。我想这是它的主要优点(缺点)吧?

2guxujil

2guxujil8#

使用@Robert代码,我将Hashie::Mash添加到基准测试项中,并得到以下结果:

user     system      total        real
Hashie::Mash slow      3.600000   0.000000   3.600000 (  3.755142)
Hashie::Mash fast      3.000000   0.000000   3.000000 (  3.318067)
OpenStruct slow       11.200000   0.010000  11.210000 ( 12.095004)
OpenStruct fast       10.900000   0.000000  10.900000 ( 12.669553)
Struct slow            0.370000   0.000000   0.370000 (  0.470550)
Struct fast            0.140000   0.000000   0.140000 (  0.145161)
dgiusagp

dgiusagp9#

  • 实际上不是对问题的回答,但如果你关心性能,这是一个非常重要的考虑因素 *。请注意,每次创建OpenStruct时,操作都会清除方法缓存,这意味着应用程序的执行速度会变慢。OpenStruct的慢不慢不仅仅是它本身是如何工作的,而是使用它们给整个应用程序带来的影响:https://github.com/charliesome/charlie.bz/blob/master/posts/things-that-clear-rubys-method-cache.md#openstructs

相关问题