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

anleb · 发布于 2012年07月03日 · 最后由 bhuztez 回复于 2012年07月14日 · 4262 次阅读
2396

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

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

共收到 63 条回复
1

还有个 BasicObject 类。

243

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

还有 included module 怎么移除.

60

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

2396

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

8

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

2396

#5楼 @hooopo super?不对吧

8

#6楼 @Anleb 搞反了。。

2396

没人解答这个问题吗?

2396

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

96

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

244

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

8

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

1107

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

146

#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。

8

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

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

243

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

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

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

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

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

大家可以发散一下.

8

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

A.include M1
A.include M2

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

1107

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

243

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

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

没解决这里面的问题.

8

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

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

#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]
96

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

243

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

96

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

243

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

96

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

243

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

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

96

#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'
243

#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>'
96

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

244

@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好处有限,麻烦多多

243

#30楼 @bhuztez

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

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

243

#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
96

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

243

#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"

这个作弊太方便了.

96

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

243

#38楼 @bhuztez 可以通过

def B.superclass
  Object
end

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

96

#39楼 @Saito 所以不一样么

243

#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]
96

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

243

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

96

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

243

#45楼 @bhuztez

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

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

96

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

243

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

96

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

96

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

243

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

96

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

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

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

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

243

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

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

96

#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的地方,这个好像没啥办法。

2396

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

2396

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

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

96

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

8

#60楼 @bhuztez 你的结论是?

1267

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

96

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

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