我有一些这样的代码:
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
类的大脑。不幸的是,没有迹象表明发生了这种情况:>>> rperson = RandomPerson()
>>> assert isinstance(rperson,Kyle) or isinstance(rperson,John)
AssertionError
因此,我们仍然没有真正将
Kyle
或John
子类化。另外,这真的是非常邪恶的。因此,除非您有充分的理由,否则请不要这样做。现在,假设您确实有充分的理由,如果您所要做的只是确保可以使用
Kyle
或John
和RandomPerson
一起使用任何类状态信息(方法和类属性),则上述解决方案应该足以满足的需要。但是,如前所述,RandomPerson
仍然不是其中一个的真正子类。据我所知,几乎没有办法在实例创建时实际上将对象的类随机子集化,也无法让该类在多个实例创建时保持状态。您将不得不伪造它。
伪造它的一种方法是使用abstract baseclass module and
RandomPerson
将John
视为Kyle
和__subclasshook__
的子类,并将其添加到Person
类中。这似乎是一个很好的解决方案,因为Person
类是一个接口(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/