【问题标题】:Subclassing in python of instantiated superclass实例化超类的python中的子类化
【发布时间】:2023-04-08 08:29:01
【问题描述】:

是否可以使用已经实例化的超类在 Python 中进行子类化?

我不完全知道如何提出问题,所以让我举个例子。假设我有一个类 Rectangle,我想构建另一个类 ColoredRectangle。但我不希望每个相同尺寸的ColoredRectangle 都成为自己的新Rectangle。因此,当我启动ColoredRectangle 时,我会将它传递给已经实例化的Rectangle

也就是说,我想要

r = Rectangle([1,2])
r_red = ColoredRectangle(r, "red")
r_blue = ColoredRectangle(r, "blue")

但是现在r_redr_blue 应该可以获取所有的矩形方法和属性了。例如假设Rectangle 有一个area() 属性。

r.area
2
r_red.area
2

r_redr_blue 应该“指向”同一个 Rectangle。我知道我可以通过写作来做到这一点:

 class ColoredRectangle(Rectangle):

      def __init__(self, rectangle, color):
          self.color = color
          self.rectangle = rectangle

但是我不得不写

  r_red.rectangle.area

这很丑。

【问题讨论】:

    标签: python inheritance subclass


    【解决方案1】:

    用矩形属性替换所有属性。它们位于__dict__ 属性中。

    import copy
    
    class Rectangle(object):
        def __init__(self, area):
            self.area = area
    
    class ColoredRectangle(Rectangle):
        def __init__(self, rectangle, color):
            self.__dict__ = copy.deepcopy(rectangle.__dict__)
            self.color = color
    

    【讨论】:

    • 我想举一个简单的例子。假设 Rectangle 对应的属性和方法有很多。我不想全部覆盖它们。
    • @Ben 然后使用self.__dict__ = rectangle.__dict__。我改进答案
    • @TomaszJakubRup。那么你会把你的color 属性放在哪里呢?
    • @ppperry self.color = color?
    • 当您使用两个具有相同基本 Rectangle 的 ColoredRectangles 运行此代码时,第一个的颜色会丢失。
    【解决方案2】:

    继承

    继承在 python 中是一件非常好的事情,我认为你不必求助于getattr hacks,如果你想要这些,请向下滚动。

    您可以强制类字典引用另一个对象:

    class Rectangle(object):
        def __init__(self, width, height):
            self.width = width
            self.height = height
        def area(self):
            return self.width * self.height
    
    
    class ColoredRectangle(Rectangle):
        def __init__(self, rect, color):
            self.__dict__ = rect.__dict__
            self.color = color
    
    
    rect = Rectangle(3, 5)
    crect = ColoredRectangle(rect, color="blue")
    print crect.width, crect.height, crect.color
    #3 5 blue
    

    这两个将引用同一个Rectangle 对象:

    crect.width=10
    print rect.width, rect.height
    #10 5
    

    这是一个关于元编程的精彩演讲,虽然它的标题暗示了 Python3,但它也适用于 python 2.x:David Beazley - Python3 Metaprogramming


    getattr黑客攻击

    但是,如果出于任何原因,您希望多个 ColoredRectangle 引用同一个基 Rectangle,那么这些将相互冲突:

    eve = Rectangle(3, 5)
    kain = ColoredRectangle(eve, color="blue")
    abel = ColoredRectangle(eve, color="red")
    print eve.color, kain.color, abel.color
    #red red red
    

    如果您想要不同的“代理对象”,它们可以从基础Rectangle 获取属性但不会相互干扰,您必须求助getattrhacking,也很有趣:

    class ColoredRectangle(Rectangle):
        def __init__(self, rect, color):
            self.rect = rect
            self.color = color
        def __getattr__(self,attr):
            return getattr(self.rect,attr)
    eve = Rectangle(3, 5)
    

    这样可以避免干扰:

    kain = ColoredRectangle(eve, color="blue")
    abel = ColoredRectangle(eve, color="red")
    print kain.color, abel.color
    #blue red
    

    关于__getattr____getattribute__

    getattrgetattribute 之间的主要区别在于 getattr 仅在没有以通常方式找到该属性时才被调用。它有利于实现缺失属性的后备, 并且可能是您想要的两个之一。 source

    因为__getattr__ 只会处理未找到的属性,所以您也可以部分更新您的代理,这可能会造成混淆:

    kain.width=10
    print eve.area(), kain.area(), abel.area()
    # 15 50 15
    

    为避免这种情况,您可以覆盖 __setattr__:

    def __setattr__(self, attr, value):
        if attr == "color":
            return super(ColoredRectangle,self).setattr(attr,value)
        raise YourFavoriteException
    

    【讨论】:

    • 太棒了!我只是在研究 getattrsetattr 并想知道类字典以及如何使用它。使用__dict____getattr__ 和/或__setattr__ 有什么区别?并感谢谈话链接。我一直在寻找好的参考。
    • 三个小时,但 Beazley 是一位了不起的演讲者。它有整个有趣的包:Regex、XML、exec、JIT 代码生成、导入黑客等等......
    • 如果Rectangle 类定义__slots__ 或用C 编写,则__dict__ 示例不起作用。
    • @SebastianWozny 我不允许指出其他缺点。并非所有 python 类型都有一个\__dict__ 属性并在其中公开所有数据。其实代理ColoredRectangle就是这样一个不合规的类型。
    • 为什么强制类字典有你提到的副作用? self.__dict__ = rect.__dict__这行是否有设置两个字典在内存中相等的效果。因此,如果您将一个属性添加到任一字典中,您会将其添加到两个字典中吗?
    【解决方案3】:

    您似乎要求做的是将属性访问重定向到底层Rectangle 对象。 __getattr__ 方法可以为您做到这一点。

    class ColoredRectangle(object):
      def __init__(self, rectangle, color):
          self.color = color
          self.rectangle = rectangle
      def __getattr__(self,attr):
           return getattr(self.rectangle,attr)
    

    【讨论】:

    • 我认为这是最好的解决方案。
    • 也许你应该解释一下 rectangle 类属性的用途。
    • rectangle 类属性的原因是服务器作为默认值,以便__setattr__ 魔术方法知道它是从__init__ 调用的,并且该类仍在初始化。
    • 当然,但是这样的信息属于答案本身,而不仅仅是评论。 FWIW,我会说类似“rectangle 类属性强制__setattr____init__ 期间设置实例属性.color.rectangle 时调用默认.rectangle,当没有' t 还有一个 .rectangle 实例属性来重定向 __setattr__ 调用。也许您还应该明确提到,如果将任何新属性添加到 ColoredRectangle 实例,它们实际上将附加到底层 Rectangle 实例.
    • 这种行为似乎是我宁愿避免的。我不应该能够从ColoredRectangle 更改Rectangle 的属性。是否可以防止这种情况发生?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-09-28
    • 1970-01-01
    • 1970-01-01
    • 2013-12-15
    • 1970-01-01
    • 1970-01-01
    • 2016-06-13
    相关资源
    最近更新 更多