python类工厂继承random paren

2024-06-01 06:26:42 发布

您现在位置:Python中文网/ 问答频道 /正文

我有这样的代码:

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"

我如何在满足以下要求的情况下实现随机人:

  • {{cd2>调用对象^必须}。在
  • RandomPerson应该随机地子类John或{}。在

Tags: tofromselfnewdefdriverpassdrive
3条回答

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

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

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

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

可以这样切换对象的类:

^{pr2}$

但是,这样做意味着对RandomPerson的调用不会根据您的要求返回RandomPerson的实例,而是返回Kyle或{}的实例。所以这是不可能的。在

下面是一种获取RandomPerson对象的方法,该对象的行为类似于Kyle或{},但不是

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

所以我们仍然没有真正的子类Kyle或{}。而且,这真的很邪恶。所以除非你有充分的理由,否则请不要这么做。在

现在,假设您确实有一个很好的理由,如果您所追求的是确保可以使用Kyle或{}中的任何类状态信息(方法和类属性),那么上面的解决方案就足够了。然而,如前所述,RandomPerson仍然不是其中一个真正的子类。在

正如我所知,没有办法在实例创建时随机地将一个对象的类划分为子类,也没有办法让该类在多个实例创建过程中保持状态。你得假装一下。在

伪造它的一种方法是允许使用abstract baseclass module and ^{}RandomPerson视为John和{}的子类,并将其添加到您的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-尽管它在技术上不是一个子类,但它被认为是Kyle或{}的子类,并且它也共享KyleJohn状态。事实上,每次创建一个新实例(或者RandomPerson.identity被更改)时,它将在这两个实例之间随机地来回切换。这样做的另一个效果是:如果您有多个RandomPerson实例,它们都共享该时刻RandomPerson的状态,即,rperson1可能开始是Kyle,然后当{}被实例化时,rperson2和{}都可以是John(或者它们都可以是Kyle,然后在创建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

您只需实现RandomPerson类,就可以拥有一个名为_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)

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

^{pr2}$

你在你的most recent comment on my other answer中说:

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 RandomPerson(): # Doing it this way, you also don't need to inherit from Person
    def __new__(self):
        return random.choice((Kyle,John))()

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

^{pr2}$

相关问题 更多 >