use*_*203 5 python inheritance factory python-2.7
我有一些像这样的代码:
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"
Run Code Online (Sandbox Code Playgroud)
我如何实现RandomPerson,具有以下要求:
person = RandomPerson()必须返回一个RandomPerson对象.RandomPerson应该是子类John或Kyle随机的.在我原来的答案(我删除了它,因为它完全错误)中,我说我会考虑这样做:
class RandomPerson(Person):
def __init__(self):
rand_person = random.choice((John, Kyle))()
self.__dict__ = rand_person.__dict__
Run Code Online (Sandbox Code Playgroud)
这种方式是对 Python Borg 惯用法的改编;这个想法是,关于一个对象的所有重要信息都包含在它的__dict__.
然而,这只在覆盖同一类的对象时才有效(这就是您在 Borg 惯用法中所做的事情);该对象__dict__仅包含与对象实例相关的状态信息,而不包含与对象类相关的状态信息。
可以像这样切换对象的类:
class RandomPerson(Person):
def __init__(self):
rand_person = random.choice((John, Kyle))
self.__class__ = rand_person
Run Code Online (Sandbox Code Playgroud)
但是,这样做意味着调用RandomPerson不会根据您的要求返回实例RandomPerson,而是返回实例Kyle或 的实例John。所以这是不行的。
这是一种获取RandomPerson行为类似于 or 的对象的Kyle方法,John但不是:
class RandomPerson(Person):
def __new__(cls):
new = super().__new__(cls)
new.__dict__.update(random.choice((Kyle,John)).__dict__)
return new
Run Code Online (Sandbox Code Playgroud)
这个 - 与 Borg 习惯用法非常相似,除了用类而不是实例对象来实现,并且我们只是复制所选类字典的当前版本- 确实非常邪恶:我们已经对类进行了脑叶切除RandomPerson,并(随机)卡住了一个Kyle或一个类的大脑John就位。不幸的是,没有迹象表明这种情况发生了:
>>> rperson = RandomPerson()
>>> assert isinstance(rperson,Kyle) or isinstance(rperson,John)
AssertionError
Run Code Online (Sandbox Code Playgroud)
所以我们还没有真正子类化Kyle或者John进行子类化。而且,这实在是太邪恶了。所以请不要这样做,除非你有充分的理由。
现在,假设您确实有充分的理由,如果您所追求的只是确保可以使用来自Kyle或John与RandomPerson. 然而,如前所述,RandomPerson它仍然不是两者的真正子类。
据我所知,几乎没有办法在实例创建时实际随机地子类化对象的类,并使该类在多个实例创建中维护状态。你将不得不伪造它。
伪造它的一种方法是允许RandomPerson被视为 的子类John并Kyle使用抽象基类模块 和__subclasshook__,并将其添加到您的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
Run Code Online (Sandbox Code Playgroud)
现在RandomPerson- 尽管它在技术上不是子类 - 被认为是Kyleor的子类John,并且它也共享or的状态。事实上,每次创建新实例(或更改时),它都会在两者之间随机地来回切换。以这种方式做事的另一个效果是:如果你有多个实例,它们都共享那一刻发生的任何事情的状态- 即,可能开始是,然后当实例化时,两者AND都可以是(或者它们可以两者都是,然后切换到创建时)。KyleJohnRandomPerson.identityRandomPersonRandomPersonrperson1Kylerperson2rperson2rperson1JohnKyleJohnrperson3
不用说,这是非常奇怪的行为。事实上,这很奇怪,我怀疑你的设计需要彻底修改。我真的不认为有一个很好的理由这样做(除了可能对某人开一个糟糕的玩笑之外)。
如果您不想将此行为混合到您的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
Run Code Online (Sandbox Code Playgroud)