python类工厂继承随机父级

时间:2015-06-17 18:26:23

标签: python python-2.7 inheritance factory

我有一些像这样的代码:

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

3 个答案:

答案 0 :(得分:1)

您可以实现RandomPerson类来拥有一个名为_my_driver的成员或其他任何您想要的成员。您只需从drive方法调用他们的RandomPerson.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)

或者,如果您想更加严格地确保该类具有与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

答案 1 :(得分:1)

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

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。而且,这真的很邪恶。所以除非你有充分的理由,否则请不要这样做。

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

接近我可以说,在实例创建时无法实际随机子类化对象的类,并使类在多个实例创建中保持状态。你将不得不假装它。

伪造它的一种方法是允许RandomPerson使用abstract baseclass module and __subclasshook__被视为JohnKyle的子类,并将其添加到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

现在RandomPerson - 虽然它在技术上不是一个子类 - 被认为是 KyleJohn的子类,它也共享< em>状态 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

答案 2 :(得分:0)

most recent comment on my other answer中你说:

  

我会改变对象的类,就像你指出的那样:rand_person = random.choice((John, Kyle))self.__class__ = rand_person。我已将RandomPerson的方法移回PersonRandomPerson现在可以像工厂生成类一样工作。

如果我可以这样说,这是一个奇怪的选择。首先,在创建对象后交换类并不是非常pythonic(它过于复杂)。最好随机生成对象实例,而不是在事实之后分配类:

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))()