代码之家  ›  专栏  ›  技术社区  ›  intuited

实现原型或实例化类对象

  •  3
  • intuited  · 技术社区  · 14 年前

    updated 2010-06-15T09:45:00Z

    • 添加了“类作为实例”方法的示例,以及对“实例作为类”方法的解释;合并并引用了Alex Martelli的答案;

    第二种方法似乎更灵活,因为它可以应用于各种类型的现有对象,而第一种方法对于典型的用例可能更方便。

    类作为实例

    这里的想法是使用元类使实例化实际上是类,而不是对象。这种方法类似于:

    class ClassAsInstance(type):
        """ ClassAsInstance(type)\n
            >>> c = ClassAsInstance()
            >>> c.prop = 6
    
            It's sort of annoying to have to make everything a class method.
            >>> c.jef = classmethod(lambda self: self.prop)
            >>> c.jef()
            6
            >>> cc = c()
            >>> cc.jef()
            6
    
            But it works.
            >>> c.prop = 10
            >>> cc.jef()
            10
            >>> c.jef = classmethod(lambda self: self.prop * 2)
            >>> cc.jef()
            20
        """
        def __new__(self):
            return type(self.__name__ + " descendant", (self, ), {})
    

    实例作为类

    使用这种方法,我们的想法是使用 type 构造函数从对象创建类。这一点在 Alex Martelli's answer ,尽管他用于此方法的示例实现了复制原型,而不是允许后代继承其原型的后续更改。

    def createDescendant(obj):
        return type(obj.__class__.__name__ + " descendant", (obj.__class__, ), obj.__dict__)()
    

    它的工作方式有点像javascript-y:对给定对象的更改不会影响它的后代,但会影响父对象的 __class__ (就像一个javascript prototype )威尔。我想这是因为 类型 建造师 副本 obj.__dict__ 而不是在某种mro-ish计划中引用它。

    我试图实现一个改进的版本,允许真正的原型继承,其中对象将继承父对象的更新。这个想法是分配原型对象的 __dict__ 属性设置为新创建的类的同一属性,即成为子对象的类的属性。

    无法分配给;此限制也适用于派生自 类型 类型 . 这可能会产生其他问题,比如亚历克斯在回答的第一部分提到的授权方法固有的问题。

    Alex还建议了第三种方法,即委托,其中对象的状态通过 __getattr__ 神奇的方法。同样,请参阅Alex的回答以获取示例,以及关于此方法局限性的详细信息。

    2 回复  |  直到 7 年前
        1
  •  4
  •   Alex Martelli    14 年前

    如果您需要在将来对原型对象的修改透明地反映在所有“后代”中,那么您必须求助于显式委托。在普通方法中,通过 __getattr__ 例如,来源于:

    class FromPrototype(object):
      def __init__(self, proto):
        self._proto = proto
      def __getattr__(self, name):
        return getattr(self._proto, name)
    

    …只要你同时继承 状态 行为 . 不幸的是,原型中的非重写方法不会感知到当前对象中可能已被重写的任何状态。此外, 在类而不是实例中查找的方法(具有以双下划线开头和结尾的神奇名称的方法)不能简单地以这种方式委托。因此,解决这些问题可能需要大量的工作。

    如果您不关心“无缝继承”未来对原型的修改,但可以在“原型继承”时对后者进行快照,则更简单:

    import copy
    
    def proto_inherit(proto):
      obj = copy.deepcopy(proto)
      obj.__class__ = type(obj.__class__.__name__, (obj.__class__,), {})
      return obj
    

    proto_inherit )在不影响任何其他对象的情况下(对于普通方法,您可以在类或实例上设置它们,尽管始终使用该类会更加规则和一致)。

        2
  •  0
  •   Daniel Mahler    9 年前

    主要的改进是

    1. 当继承的成员是方法时,则返回具有相同基础函数但绑定到原始调用对象的方法。

      ……只要你也继承了原型的状态,就不会 只是行为。不幸的是,来自 原型不会感知到任何可能在中被覆盖的状态 当前对象。

    一个限制是 Proto

    代理.py

    import types
    import inspect
    
    class Proto(object):
      def __new__(self, proto, *args, **kw):
        return super(Proto, self).__new__(self, *args, **kw)
      def __init__(self, proto, *args, **kw):
        self.proto = proto
        super(Proto, self).__init__(*args, **kw)
      def __getattr__(self, name):
        try:
          attr = getattr(self.proto, name)
          if (inspect.ismethod(attr) and attr.__self__ != None):
            attr = types.MethodType(attr.__func__, self)
          return attr
        except AttributeError:
          return super(Proto, self).__getattr__(name)
    

    号召 b.getY() 下面说明了Alex的观点,如果 FromPrototype 他的答案是用阶级来代替的

    from delegate import Proto
    
    class A(Proto):
      x = "This is X"
      def getY(self):
        return self._y
    
    class B(Proto):
      _y = "This is Y"
    
    class C(object):
      def __getattr__(self, name):
        return "So you want "+name
    
    class D(B,C):
      pass
    
    if __name__ == "__main__":
      a = A(None)
      b = B(a)
      print b.x
      print b.getY()
      d = D(a)
      print d.x
      print d.getY()
      print d.z