python class 工厂继承随机父

python class factory inherit random parent

我有一些这样的代码:

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,满足以下要求:

您可以只实施 RandomPerson class 来拥有一个名为 _my_driver 的成员或您想要的任何其他成员。您只需从 RandomPerson.drive 方法调用他们的 drive 方法。它可能看起来像这样:

    class RandomPerson(Person):
    # instantiate either John or Kyle, and inherit it.
    def __init__(self):
        self._my_person = John() if random.random() > 0.50 else Kyle()
    def drive(self, f, t):
        self._my_person.drive(f,t)

或者,如果您想更严格地确保 class 具有与 KyleJohn 完全相同的方法,您可以在构造函数中设置方法像下面这样:

class RandomPerson(Person):
    # instantiate either John or Kyle, and inherit it.
    def __init__(self):
        self._my_person = John() if random.random() > 0.50 else Kyle()
        self.drive = self._my_person.drive

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

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

这种方式是对Python Borg idiom的改编;这个想法是关于一个对象的所有重要内容都包含在它的 __dict__ 中。

但是,这仅在覆盖 相同 class 的对象时有效(这就是您在 Borg 习语中所做的);对象 __dict__ 只包含与对象实例有关的状态信息, 对象 class。

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

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

但是,这样做意味着对 RandomPerson 的调用将不会 return 根据您的要求 RandomPerson 的实例,而是 KyleJohn。所以这是不行的。

这是一种获取 RandomPerson 对象的方法,该对象 行为 类似于 KyleJohn,但 不是't

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

这个 - 与 Borg 习语非常相似,除了用 classes 而不是实例对象来做,我们只复制 当前版本 选择 class dict - 真的很邪恶:我们已经对 RandomPerson class 进行了脑叶切除术并且(随机地)卡住了 KyleJohn [=115] 的大脑=]到位。不幸的是,没有迹象表明这发生了:

>>> rperson = RandomPerson()
>>> assert isinstance(rperson,Kyle) or isinstance(rperson,John)
AssertionError

所以我们仍然还没有真正class编辑KyleJohn。而且,这真的很邪恶。所以除非你有充分的理由,否则请不要这样做。

现在,假设您确实有充分的理由,上述解决方案 应该足够好 如果您所追求的只是确保您可以使用任何 class 来自 KyleJohnRandomPerson 的状态信息(方法和 class 属性)。但是,如前所述,RandomPerson 仍然不是两者的真正子class。

据我所知,实际上没有办法在创建实例时随机子class 一个对象的class 并让class 在创建多个实例时保持状态。你将不得不伪造它。

一种伪造它的方法是使用 abstract baseclass module and __subclasshook__ 允许 RandomPerson 被视为 JohnKyle 的子 class,并添加对你的 Person class。这看起来像是一个很好的解决方案,因为 Person class 是一个接口,无论如何都不会直接使用。

这是一种方法:

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 - 尽管从技术上讲它不是子class - 被认为是 [=20 的子class =]或John,它也共享KyleJohn状态。事实上,每次创建新实例(或更改 RandomPerson.identity 时),它都会随机地在两者之间来回切换。这样做的另一个效果是:如果你有多个 RandomPerson 个实例, 它们都会 共享 任何 RandomPerson 的状态恰好是 在那一刻——即 rperson1 可能开始是 Kyle,然后当 rperson2 被实例化时, rperson2rperson1 都可以是 John(或者它们都可以是 Kyle,然后在创建 rperson3 时切换到 John)。

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

如果您不想将此行为混入您的 Person class,您也可以单独进行:

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

在你的 你说:

I'm gonna go with changing the class of the object, like you pointed out: rand_person = random.choice((John, Kyle)) and self.__class__ = rand_person. I've moved the methods of RandomPerson back into Person, and RandomPerson works now like a factory-ish generating class.

如果我可以这么说,这是一个奇怪的选择。首先,在对象创建后换出 class 似乎不是很 pythonic(它过于复杂)。最好随机生成对象实例,而不是事后分配 class:

class RandomPerson(): # Doing it this way, you also don't need to inherit from Person
    def __new__(self):
        return random.choice((Kyle,John))()

其次,如果代码已经过重构,您不再需要 RandomPerson 对象,为什么还要一个呢?只需使用工厂函数:

def RandomPerson():
    return random.choice((Kyle,John))()