我有一些像这样的代码:
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
应随机选择John
或Kyle
。答案 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)
或者,如果您想更加严格地确保该类具有与Kyle
或John
完全相同的方法,您可以在构造函数中设置方法,如下所示:
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
的实例,而是Kyle
或John
。所以这是不行的。
以下是获取行为的RandomPerson
对象的方法,例如Kyle
或John
,但不是:
class RandomPerson(Person):
def __new__(cls):
new = super().__new__(cls)
new.__dict__.update(random.choice((Kyle,John)).__dict__)
return new
这个 - 非常类似于Borg习语,除了用类而不是实例对象做它而我们只复制所选类dict的当前版本 - 真的很邪恶:我们已经对RandomPerson
类进行了分类,并且(随机)将Kyle
或John
类的大脑固定住了。不幸的是,没有任何迹象表明这种情况发生了:
>>> rperson = RandomPerson()
>>> assert isinstance(rperson,Kyle) or isinstance(rperson,John)
AssertionError
因此,仍尚未真正隐藏Kyle
或John
。而且,这真的很邪恶。所以除非你有充分的理由,否则请不要这样做。
现在,假设你确实有充分的理由,上面的解决方案应该足够好如果您所有人都确保您可以使用任何类状态信息(方法和类属性)来自Kyle
或John
RandomPerson
。但是,如前所述,RandomPerson
仍然不是两者的真正子类。
接近我可以说,在实例创建时无法实际随机子类化对象的类,并使类在多个实例创建中保持状态。你将不得不假装它。
伪造它的一种方法是允许RandomPerson
使用abstract baseclass module and __subclasshook__
被视为John
和Kyle
的子类,并将其添加到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
或John
的子类,它也共享< em>状态 Kyle
或John
。实际上,每次创建新实例(或更改RandomPerson.identity
时),它将在两者之间来回切换。以这种方式做事的另一个影响:如果您有多个RandomPerson
个实例,他们都分享那个RandomPerson
恰好是的状态时刻 - 即,rperson1
可能最初为Kyle
,然后在rperson2
实例化时,rperson2
和rperson1
都可能为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
的方法移回Person
,RandomPerson
现在可以像工厂生成类一样工作。
如果我可以这样说,这是一个奇怪的选择。首先,在创建对象后交换类并不是非常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))()