新手问题 忽然想知道如何取消继承的类 (没人关注吗)

anleb · 2012年07月03日 · 最后由 bhuztez 回复于 2012年07月14日 · 7574 次阅读

如题,一个类继承另一个类,如何取消继承关系,别告诉我把“<”符号删掉,如果我想取消继承 Object 类呢?

问题不是关注点,我觉得 这种思维方式 和 讨论的热情是关注点。

还有个 BasicObject 类。

这个我还真不知道怎么 remove inherited class.

还有 included module 怎么移除。

ruby 貌似把删除重点放在方法而不是类或者 module 上!

#1 楼 @Rei我知道,关键如何移出

动态移除真不知道,移除继承的方法查找链用 super

#5 楼 @hooopo super?不对吧

没人解答这个问题吗?

我想到一个答案:写类的时候,直接显示 继承 BasicObject,应该可以做到,不继承 object

看了一下你的问题,再看了一下你想到的答案,真让我茅厕顿开啊。

没想过这个问题,楼主能提供一个应用场景么?

楼上两位把我要说的都说了。。

@hooopo@Anleb 这孩子只是想刨根问底一下 - -

#11 楼 @fsword #12 楼 @hooopo #13 楼 @jasl 让一个类直接继承 BasicObject 是有应用场景的,尤其是在写一个 Builder-Style 的 DSL 时,我们需要一个比较干净的基类,以免对 Builder 的行为造成干扰。

  1. 在 Ruby 1.8 时,因为根类就是 Object,而 Object 有大量的方法,所以子类需要 undef 掉不相关的 method。有个 gem 就是专门做这个的:BlankSlate
  2. 在 Ruby 1.9 时,BasicObject 就是一个相对干净的类,在写 Builder-Style 的 DSL 时,就可以用 BasicObject 来做基类了。

拿 Builder 这个 gem 来说明一下:

module Builder
  class XmlBase < BlankSlate
    ......
  end
end
module Builder
  if Object::const_defined?(:BasicObject)
    BlankSlate = ::BasicObject
  else
    require 'blankslate'
    BlankSlate = ::BlankSlate
  end
end

其实,BlankSlate 这个 gem 就是从 Builder 中剖离了出来的,并单独做成了一个 gem。

#14 楼 @skandhas Rails 里做委托/代理相关的特性时都是用的 BlankSlate 类似实现。目的是在 method miss 时候调用更少的 stack。

但是从楼主的描述看,我没想到他是要这个。。看了他自己的回答我也是“茅厕顿开”

#15 楼 @hooopo 我们转换一下这个问题吧。

就像最开始说的那样.. 怎样能取消一个已有类的继承关系?或者 mixin module 的关系..

现在看来,大家的解决方案都是 undef 掉 mixin method, 或者 undef 掉 继承的 method.

但是实际上没有解决本质问题。

实际上这个在 Java 里面挺好解决的。Java 可以把老的类从 classloader 里面清理掉。重新 load 一个新的.. Ruby 没有 classloader 机制。貌似 cache 是不清理的。

大家可以发散一下。

#16 楼 @Saito 除了 undef 还可以 redef。 方法查找链是 obj -> mix-in -> farthor_obj 其中 mix-in 只能有一个,所以有时候可以这样:

A.include M1
A.include M2

M2 的同名方法直接覆盖了 M1 的同名方法。

@skandhas 嗯,元编程里提到过白板类的场景

#17 楼 @hooopo 没有解决本质问题啊。

[0] pry(main)> Array.included_modules
=> [Enumerable, PP::ObjectMixin, Kernel]

没解决这里面的问题。

#19 楼 @Saito 是的。。我只是提一下。Java 的 classloader 可以做什么? 这样?

Array.included_modules
=> [Enumerable, PP::ObjectMixin, Kernel]
java class loader run!
Array.included_modules
=> []

#20 楼 @hooopo 类似这样:

A.included_modules
=> [Enumerable, PP::ObjectMixin, Kernel]
ClassLoader.load(A.class)
=> [A.class]
ClassLoader.unload(A.class)
=> []
File.edit(A.class)
=> ooxx
ClassLoader.load(A.class)
=> [PP::ObjectMixin, Kernel]

#21 楼 @Saito 这个只是重新生成了一个类吧...

#22 楼 @bhuztez 是的,用新的 classloader load 一个新的同名类。

#23 楼 @Saito 所以还是不行嘛

#24 楼 @bhuztez 求助,Python 行么?

#25 楼 @Saito 明显不行啊,最多只能动态生成一个新类替换进去...

#26 楼 @bhuztez 能做到替换么?只是覆盖吧。覆盖的话也是做不到清除继承信息。

Ruby 里面我也不知道怎么做替换.. 我只知道可以覆盖。

#27 楼 @Saito

就像这样直接替换掉啊

>>> class A(object):
...     a = 1
... 
>>> class B(A):
...     pass
... 
>>> b = B()
>>> b.a
1
>>> class B(object):
...     pass
... 
>>> b.__class__ = B
>>> b.a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'B' object has no attribute 'a'

#28 楼 @bhuztez

[1] pry(main)> class A
[1] pry(main)* end  
=> nil
[2] pry(main)> class B < A
[2] pry(main)* end  
=> nil
[3] pry(main)> B.superclass
=> A
[4] pry(main)> class B < Object
[4] pry(main)* end  
=> nil
[5] pry(main)> B.superclass
=> A
[6] pry(main)> class B < BasicObject
[6] pry(main)* end  
TypeError: superclass mismatch for class B
from (pry):9:in `<main>'

#29 楼 @Saito 得把 instance 也改掉啊,你这样光改个 class 又没用的

@saito 如果是实现白板类,那么 classloader 很有用,它就是搞了一个 new world,所以很干净

不过如果仅仅是取消继承类,那么 classloader 解决不了这个问题,比如你希望的是类 X 不再继承类 Y,那么我认为你的目的是让所有用到 X 的地方都 assert 下面的表达式

X.new.is_a? Y == false

然而 java 的 cl 不是这个意思,它是 build 了一个 new world,在这里面 Y 不是 X 的父类(原来的那个 world 里,Y 依然是 X 的父类)

换个角度看,classloader 机制导致的结果是,我们不能通过类名来识别一个类,而是要用 classloader + 类名组合来识别,在这个意义上,我们不能改变类的继承关系(只能创建新的类——用 cl+ 类名标识)。所以我一直认为 classloader 好处有限,麻烦多多

#30 楼 @bhuztez

>>> class A:
...   pass
... 
>>> class B(A):
...   pass
... 
>>> B.__bases__
(<class __main__.A at 0x10a7fdce8>,)
>>> class B(object):
...   pass
... 
>>> B.__bases__
(<type 'object'>,)

#32 楼 @bhuztez 哈哈哈哈,骗子

#30 楼 @bhuztez #33 楼 @Saito

[1] pry(main)> class A
[1] pry(main)* end  
=> nil
[2] pry(main)> class B < A
[2] pry(main)* end  
=> nil
[3] pry(main)> B.superclass
=> A
[4] pry(main)> B = Class.new
(pry):6: warning: already initialized constant B
=> B
[5] pry(main)> class B < Object
[5] pry(main)* end  
=> nil
[6] pry(main)> B.superclass
=> Object

#35 楼 @Saito Ruby 里面 b.__class__ = B 咋实现?

#36 楼 @bhuztez

[7] pry(main)> b = B.new
=> #<B:0x007ff5b192e6a0>
[8] pry(main)> b.class
=> B
[10] pry(main)> def b.class
[10] pry(main)*   "C"
[10] pry(main)* end  
=> nil
[11] pry(main)> b.class
=> "C"

这个作弊太方便了。

#37 楼 @Saito 这个不算啊,Python 里面 b.__class__ = B 是起作用的,用来作弊的__instancecheck____subclasscheck__

#38 楼 @bhuztez 可以通过

def B.superclass
  Object
end

改变继承关系。但是继承到的 方法什么的都是还在的。

#39 楼 @Saito 所以不一样么

#40 楼 @bhuztez

[1] pry(main)> class A
[1] pry(main)*   def a;end
[1] pry(main)* end  
=> nil
[2] pry(main)> class B < A
[2] pry(main)* end  
=> nil
[3] pry(main)> B.new.methods - Object.new.methods
=> [:a]
[4] pry(main)> class C
[4] pry(main)*   def c; end
[4] pry(main)* end  
=> nil
[5] pry(main)> B = Class.new C
(pry):10: warning: already initialized constant B
=> B
[6] pry(main)> B.new.methods - Object.new.methods
=> [:c]

#41 楼 @Saito 你这不算啊,你后面的 B 是新建出来的啊

#43 楼 @bhuztez 替换覆盖重名的.. Just a little trick.

#44 楼 @Saito 反正就是 Ruby 不能直接把 object 的 class 改了,Python 可以...

#45 楼 @bhuztez

bhuztez 45 楼,于 3 分钟前回复 喜欢 #44 楼 @Saito 反正就是 Ruby 不能直接把 object 的 class 改了,Python 可以...

小时候院子里小伙伴经常这么说...

#46 楼 @Saito 你不觉得不能改会有问题么,每载入一份新版本的代码,内存占用就多那么一点点...

#47 楼 @bhuztez 有 GC, 萌大奶...

#49 楼 @Saito GC 也不行啊,因为已经存在的 object 会引用旧版的 class,只要那个 object 没死,旧版的 class 就不会被干掉啦...

#48 楼 @hooopo 不是,Python 和 Ruby 的官方实现,从 C 层面上看,object 都是有个一个类型指针的。只是 Ruby 不让你在 Ruby 代码里替换这个指针,就是这样...

#50 楼 @bhuztez 话说 Python 直接替换 class 实现。以前的 old class instance 难道不会挂?

#52 楼 @Saito 你说的挂是啥概念?

instance 需要你主动去替换的,换掉 instance 的 class 之后,旧版的 class 就可以被回收了

>>> class A(object):
...     pass
... 
>>> a = A()
>>> class B(object):
...     pass
... 
>>> a.__class__ = B

#53 楼 @bhuztez 有过有个 object 使用了旧的实例。那么这个 object 还能调用 旧的实例里面的方法么?

#55 楼 @bhuztez 好吧。如果只是这个例子。

a.__class__ = Ba = B() 不就没区别了。

#56 楼 @Saito 有区别啊

>>> class A(object):
...     pass
... 
>>> old = a = A()
>>> class B(object):
...     pass
... 
>>> a.__class__ = B
>>> a == old
True
>>> a = B()
>>> a == old
False

直接改 __class__ ,引用 a 的地方,你不用去改。生成一个新的 object,你得改所有引用旧的 object 的地方,这个好像没啥办法。

#15 楼 @hooopo 一个东西创造出来 肯定有它的道理的,哈哈

#17 楼 @hooopo MIX-IN 多次 Include 可不是 只有 1 个方法,方法被覆盖咯,他只是插在了 本身类与上一个 Module 的中间,查找的时候 先被找到,先被执行而已

obj---向右--隐藏类(自己的单件类)---本身类---最后一次 include 的 Module-----上一次 Module---等等

#48 楼 @hooopo 想起来了,那个强弱类型说法不对啊。弱类型是指会有隐式类型转换,我感觉几乎所有语言都是大部分强类型,小部分弱类型。Ruby 和 C 没有本质不同啊,Ruby 在运行期每个 object 有个指向类型的指针,而 C 你可以理解为在编译器有这么个指针。只是 Ruby 不允许你直接改类型,C 允许你改而已。

#60 楼 @bhuztez 你的结论是?

匿名 #62 2012年07月14日

啊哈,每天都听到有人聊 @hooopo 声音小小的

#61 楼 @hooopo 这个就是结论啊...

需要 登录 后方可回复, 如果你还没有账号请 注册新账号