python类工厂继承随机父级

use*_*203 5 python inheritance factory python-2.7

我有一些像这样的代码:

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"
Run Code Online (Sandbox Code Playgroud)

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

  • 调用person = RandomPerson()必须返回一个RandomPerson对象.
  • RandomPerson应该是子类JohnKyle随机的.

Ric*_*ica 2

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

class RandomPerson(Person):
    def __init__(self):
        rand_person = random.choice((John, Kyle))()
        self.__dict__ = rand_person.__dict__
Run Code Online (Sandbox Code Playgroud)

这种方式是对 Python Borg 惯用法的改编;这个想法是,关于一个对象的所有重要信息都包含在它的__dict__.

然而,这只在覆盖同一类的对象时才有效(这就是您在 Borg 惯用法中所做的事情);该对象__dict__仅包含与对象实例相关的状态信息,而不包含与对象类相关的状态信息。

可以像这样切换对象的类:

class RandomPerson(Person):
    def __init__(self):
        rand_person = random.choice((John, Kyle))
        self.__class__ = rand_person
Run Code Online (Sandbox Code Playgroud)

但是,这样做意味着调用RandomPerson不会根据您的要求返回实例RandomPerson,而是返回实例Kyle或 的实例John。所以这是不行的。

这是一种获取RandomPerson行为类似于 or 的对象Kyle方法,John不是

class RandomPerson(Person): 
    def __new__(cls):
        new = super().__new__(cls)
        new.__dict__.update(random.choice((Kyle,John)).__dict__)
        return new
Run Code Online (Sandbox Code Playgroud)

这个 - 与 Borg 习惯用法非常相似,除了用类而不是实例对象来实现,并且我们只是复制所选类字典的当前版本- 确实非常邪恶:我们已经对类进行了脑叶切除RandomPerson,并(随机)卡住了一个Kyle或一个类的大脑John就位。不幸的是,没有迹象表明这种情况发生了:

>>> rperson = RandomPerson()
>>> assert isinstance(rperson,Kyle) or isinstance(rperson,John)
AssertionError
Run Code Online (Sandbox Code Playgroud)

所以我们没有真正子类化Kyle或者John进行子类化。而且,这实在是太邪恶了。所以请不要这样做,除非你有充分的理由。

现在,假设您确实有充分的理由,如果您所追求的只是确保可以使用来自KyleJohnRandomPerson. 然而,如前所述,RandomPerson它仍然不是两者的真正子类。

据我所知,几乎没有办法在实例创建时实际随机地子类化对象的类,并使该类在多个实例创建中维护状态。你将不得不伪造它。

伪造它的一种方法是允许RandomPerson被视为 的子类JohnKyle使用抽象基类模块 和__subclasshook__,并将其添加到您的Person类中。这看起来将是一个很好的解决方案,因为Person是一个接口,无论如何都不会被直接使用。

这是一种方法:

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
Run Code Online (Sandbox Code Playgroud)

现在RandomPerson- 尽管它在技术上不是子类 - 被认为是Kyleor的子类John,并且它也共享or的状态。事实上,每次创建新实例(或更改时),它都会在两者之间随机地来回切换。以这种方式做事的另一个效果是:如果你有多个实例,它们都共享那一刻发生的任何事情的状态- 即,可能开始是,然后当实例化时,两者AND都可以是(或者它们可以两者都是,然后切换到创建时)。KyleJohnRandomPerson.identityRandomPersonRandomPersonrperson1Kylerperson2rperson2rperson1JohnKyleJohnrperson3

不用说,这是非常奇怪的行为。事实上,这很奇怪,我怀疑你的设计需要彻底修改。我真的不认为有一个很好的理由这样做(除了可能对某人开一个糟糕的玩笑之外)。

如果您不想将此行为混合到您的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
Run Code Online (Sandbox Code Playgroud)