我有一些这样的代码:
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
随机。 最佳答案
在我最初的答案中(我删除了,因为它完全是错误的)我说我会考虑这样做:
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
类(class)到位。不幸的是,没有迹象表明发生了这种情况:>>> rperson = RandomPerson()
>>> assert isinstance(rperson,Kyle) or isinstance(rperson,John)
AssertionError
所以我们还没有真正子类化
Kyle
或 John
.而且,这真的很邪恶。所以除非你有充分的理由,否则请不要这样做。现在,假设您确实有充分的理由,上述解决方案 应该够用了如果您所追求的只是确保您可以使用来自
Kyle
的任何类状态信息(方法和类属性)或 John
与 RandomPerson
.但是,如前所述,RandomPerson
仍然不是两者的真正子类。就我所知,没有办法在实例创建时实际随机子类化一个对象的类,并使类在多个实例创建中保持状态。你将不得不伪造它。
伪造它的一种方法是允许
RandomPerson
被视为 John
的子类和 Kyle
使用 abstract baseclass module and __subclasshook__
,并将其添加到您的 Person
类(class)。这看起来是一个很好的解决方案,因为 Person
class 是一个接口(interface),无论如何都不会直接使用。这是一种方法:
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
,并且还共享了 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
关于python类工厂继承随机父级,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30899499/