我有一些这样的代码:

class Person(object):
    def drive(self, f, t):
        raise NotImplementedError

class John(Person):
    def drive(self, f, t):
        print "John drove from %s to %s" % (f,t)

class Kyle(Person):
    def drive(self, f, t):
        print "Kyle drove from %s to %s" % (f,t)

class RandomPerson(Person):
    # instansiate either John or Kyle, and inherit it.
    pass

class Vehicle(object):
    pass

class Driver(Person, Vehicle):
    def __init__(self):
        # instantiate and inherit a RandomPerson somehow
        pass

d1 = Driver()
d1.drive('New York', 'Boston')
>>> "John drove from New York to Boston"

d2 = Driver()
d2.drive('New Jersey', 'Boston')
>>> "Kyle drove from New Jersey to Boston"

我应如何实现RandomPerson,并具有以下要求:

调用person = RandomPerson()
  • 必须返回RandomPerson对象。
  • RandomPerson应该随机地将JohnKyle子类化。
  • 最佳答案

    在我的原始答案中(我删除了它是因为这完全是错误的),我说我会考虑这样做:

    class RandomPerson(Person):
        def __init__(self):
            rand_person = random.choice((John, Kyle))()
            self.__dict__ = rand_person.__dict__
    

    这种方式是对python Borg idiom的改编;这个想法是关于对象的所有重要事项都包含在其__dict__中。

    但是,这仅在覆盖相同类的对象时才起作用(这是您在Borg习语中所做的事情)。对象__dict__仅包含与对象实例有关的状态信息,不包含对象类的

    可以像这样切换出对象的类:
    class RandomPerson(Person):
        def __init__(self):
            rand_person = random.choice((John, Kyle))
            self.__class__ = rand_person
    

    但是,以这种方式进行操作将意味着对RandomPerson的调用将不会根据您的要求返回RandomPerson的实例,而是KyleJohn的实例。所以这是不行的。

    这是一种获取RandomPerson对象的方法,该对象的行为类似于KyleJohn,但不是:
    class RandomPerson(Person):
        def __new__(cls):
            new = super().__new__(cls)
            new.__dict__.update(random.choice((Kyle,John)).__dict__)
            return new
    

    这个非常类似于Borg习惯用法,除了使用类而不是实例对象来完成,并且我们仅复制所选类dict的当前版本外,这确实很邪恶:我们将RandomPerson类归类为(随机)卡住了到位的KyleJohn类的大脑。不幸的是,没有迹象表明发生了这种情况:
    >>> rperson = RandomPerson()
    >>> assert isinstance(rperson,Kyle) or isinstance(rperson,John)
    AssertionError
    

    因此,我们仍然没有真正将KyleJohn子类化。另外,这真的是非常邪恶的。因此,除非您有充分的理由,否则请不要这样做。

    现在,假设您确实有充分的理由,如果您所要做的只是确保可以使用KyleJohnRandomPerson一起使用任何类状态信息(方法和类属性),则上述解决方案应该足以满足的需要。但是,如前所述,RandomPerson仍然不是其中一个的真正子类。

    据我所知,几乎没有办法在实例创建时实际上将对象的类随机子集化,也无法让该类在多个实例创建时保持状态。您将不得不伪造它。

    伪造它的一种方法是使用abstract baseclass module and RandomPerson John视为Kyle__subclasshook__的子类,并将其添加到Person类中。这似乎是一个很好的解决方案,因为Person类是一个接口(interface),无论如何也不会直接使用。

    这是一种方法:
    class Person(object):
        __metaclass__ = abc.ABCMeta
        def drive(self, f, t):
            raise NotImplementedError
        @classmethod
        def __subclasshook__(cls, C):
            if C.identity is cls:
                return True
            return NotImplemented
    
    class John(Person):
        def drive(self, f, t):
            print "John drove from %s to %s" % (f,t)
    
    class Kyle(Person):
        def drive(self, f, t):
            print "Kyle drove from %s to %s" % (f,t)
    
    class RandomPerson(Person):
        identity = None
        def __new__(cls):
            cls.identity = random.choice((John,Kyle))
            new = super().__new__(cls)
            new.__dict__.update(cls.identity.__dict__)
            return new
    
    >>> type(RandomPerson())
    class RandomPerson
    >>> rperson = RandomPerson()
    >>> isinstance(rperson,John) or isinstance(rperson,Kyle)
    True
    

    现在RandomPerson(虽然从技术上讲不是子类),但被视为KyleJohn的子类,并且还共享KyleJohn的状态。实际上,每次创建新实例时(或更改RandomPerson.identity时),它都会在两者之间来回随机切换。这样做的另一个效果是:如果您有多个RandomPerson实例,,它们都共享那一刻碰巧是RandomPerson的状态-即rperson1可能开始是Kyle,然后在实例化rperson2时,两者rperson2rperson1可以是John(或者它们都可以是Kyle,然后在创建John时切换到rperson3)。

    不用说,这是非常奇怪的行为。实际上,它是如此的怪异,我怀疑您的设计需要进行彻底的检修。我真的不认为永远没有这样做的充分理由(除了可能对某人开个恶作剧之外)。

    如果您不想将此行为混入Person类中,也可以单独进行操作:
    class Person(object):
        def drive(self, f, t):
            raise NotImplementedError
    
    class RandomPersonABC():
        __metaclass__ = abc.ABCMeta
        @classmethod
        def __subclasshook__(cls, C):
            if C.identity is cls:
                return True
            return NotImplemented
    
    class John(Person, RandomPersonABC):
        def drive(self, f, t):
            print "John drove from %s to %s" % (f,t)
    
    class Kyle(Person, RandomPersonABC):
        def drive(self, f, t):
            print "Kyle drove from %s to %s" % (f,t)
    
    class RandomPerson(Person):
        identity = None
        def __new__(cls):
            cls.identity = random.choice((John,Kyle))
            new = super().__new__(cls)
            new.__dict__.update(cls.identity.__dict__)
            return new
    

    关于python类工厂继承随机父级,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30899499/

    10-10 04:47