python class 工厂继承随机父
python class factory inherit random parent
我有一些这样的代码:
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()
必须 return 一个 RandomPerson
对象。
RandomPerson
应随机子类化 John
或 Kyle
。
您可以只实施 RandomPerson
class 来拥有一个名为 _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)
或者,如果您想更严格地确保 class 具有与 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
在我原来的回答中(我删除了因为它完全错误)我说我会考虑这样做:
class RandomPerson(Person):
def __init__(self):
rand_person = random.choice((John, Kyle))()
self.__dict__ = rand_person.__dict__
这种方式是对Python Borg idiom的改编;这个想法是关于一个对象的所有重要内容都包含在它的 __dict__
中。
但是,这仅在覆盖 相同 class 的对象时有效(这就是您在 Borg 习语中所做的);对象 __dict__
只包含与对象实例有关的状态信息, 不 对象 class。
可以像这样切换对象的 class:
class RandomPerson(Person):
def __init__(self):
rand_person = random.choice((John, Kyle))
self.__class__ = rand_person
但是,这样做意味着对 RandomPerson
的调用将不会 return 根据您的要求 RandomPerson
的实例,而是 Kyle
或John
。所以这是不行的。
这是一种获取 RandomPerson
对象的方法,该对象 行为 类似于 Kyle
或 John
,但 不是't:
class RandomPerson(Person):
def __new__(cls):
new = super().__new__(cls)
new.__dict__.update(random.choice((Kyle,John)).__dict__)
return new
这个 - 与 Borg 习语非常相似,除了用 classes 而不是实例对象来做,我们只复制 当前版本 选择 class dict - 真的很邪恶:我们已经对 RandomPerson
class 进行了脑叶切除术并且(随机地)卡住了 Kyle
或 John
[=115] 的大脑=]到位。不幸的是,没有迹象表明这发生了:
>>> rperson = RandomPerson()
>>> assert isinstance(rperson,Kyle) or isinstance(rperson,John)
AssertionError
所以我们仍然还没有真正class编辑Kyle
或John
。而且,这真的很邪恶。所以除非你有充分的理由,否则请不要这样做。
现在,假设您确实有充分的理由,上述解决方案 应该足够好 如果您所追求的只是确保您可以使用任何 class 来自 Kyle
或 John
和 RandomPerson
的状态信息(方法和 class 属性)。但是,如前所述,RandomPerson
仍然不是两者的真正子class。
据我所知,实际上没有办法在创建实例时随机子class 一个对象的class 并让class 在创建多个实例时保持状态。你将不得不伪造它。
一种伪造它的方法是使用 abstract baseclass module and __subclasshook__
允许 RandomPerson
被视为 John
和 Kyle
的子 class,并添加对你的 Person
class。这看起来像是一个很好的解决方案,因为 Person
class 是一个接口,无论如何都不会直接使用。
这是一种方法:
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
- 尽管从技术上讲它不是子class - 被认为是 [=20 的子class =]或John
,它也共享Kyle
或John
的状态。事实上,每次创建新实例(或更改 RandomPerson.identity
时),它都会随机地在两者之间来回切换。这样做的另一个效果是:如果你有多个 RandomPerson
个实例, 它们都会 共享 任何 RandomPerson
的状态恰好是 在那一刻——即 rperson1
可能开始是 Kyle
,然后当 rperson2
被实例化时, rperson2
和 rperson1
都可以是 John
(或者它们都可以是 Kyle
,然后在创建 rperson3
时切换到 John
)。
不用说,这是非常奇怪的行为。事实上它太奇怪了,我怀疑你的设计需要彻底检修。我真的不认为有一个很好的理由去这样做(除了可能对某人开个恶作剧之外)。
如果您不想将此行为混入您的 Person
class,您也可以单独进行:
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
在你的 你说:
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 似乎不是很 pythonic(它过于复杂)。最好随机生成对象实例,而不是事后分配 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
对象,为什么还要一个呢?只需使用工厂函数:
def RandomPerson():
return random.choice((Kyle,John))()
我有一些这样的代码:
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()
必须 return 一个RandomPerson
对象。 RandomPerson
应随机子类化John
或Kyle
。
您可以只实施 RandomPerson
class 来拥有一个名为 _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)
或者,如果您想更严格地确保 class 具有与 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
在我原来的回答中(我删除了因为它完全错误)我说我会考虑这样做:
class RandomPerson(Person):
def __init__(self):
rand_person = random.choice((John, Kyle))()
self.__dict__ = rand_person.__dict__
这种方式是对Python Borg idiom的改编;这个想法是关于一个对象的所有重要内容都包含在它的 __dict__
中。
但是,这仅在覆盖 相同 class 的对象时有效(这就是您在 Borg 习语中所做的);对象 __dict__
只包含与对象实例有关的状态信息, 不 对象 class。
可以像这样切换对象的 class:
class RandomPerson(Person):
def __init__(self):
rand_person = random.choice((John, Kyle))
self.__class__ = rand_person
但是,这样做意味着对 RandomPerson
的调用将不会 return 根据您的要求 RandomPerson
的实例,而是 Kyle
或John
。所以这是不行的。
这是一种获取 RandomPerson
对象的方法,该对象 行为 类似于 Kyle
或 John
,但 不是't:
class RandomPerson(Person):
def __new__(cls):
new = super().__new__(cls)
new.__dict__.update(random.choice((Kyle,John)).__dict__)
return new
这个 - 与 Borg 习语非常相似,除了用 classes 而不是实例对象来做,我们只复制 当前版本 选择 class dict - 真的很邪恶:我们已经对 RandomPerson
class 进行了脑叶切除术并且(随机地)卡住了 Kyle
或 John
[=115] 的大脑=]到位。不幸的是,没有迹象表明这发生了:
>>> rperson = RandomPerson()
>>> assert isinstance(rperson,Kyle) or isinstance(rperson,John)
AssertionError
所以我们仍然还没有真正class编辑Kyle
或John
。而且,这真的很邪恶。所以除非你有充分的理由,否则请不要这样做。
现在,假设您确实有充分的理由,上述解决方案 应该足够好 如果您所追求的只是确保您可以使用任何 class 来自 Kyle
或 John
和 RandomPerson
的状态信息(方法和 class 属性)。但是,如前所述,RandomPerson
仍然不是两者的真正子class。
据我所知,实际上没有办法在创建实例时随机子class 一个对象的class 并让class 在创建多个实例时保持状态。你将不得不伪造它。
一种伪造它的方法是使用 abstract baseclass module and __subclasshook__
允许 RandomPerson
被视为 John
和 Kyle
的子 class,并添加对你的 Person
class。这看起来像是一个很好的解决方案,因为 Person
class 是一个接口,无论如何都不会直接使用。
这是一种方法:
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
- 尽管从技术上讲它不是子class - 被认为是 [=20 的子class =]或John
,它也共享Kyle
或John
的状态。事实上,每次创建新实例(或更改 RandomPerson.identity
时),它都会随机地在两者之间来回切换。这样做的另一个效果是:如果你有多个 RandomPerson
个实例, 它们都会 共享 任何 RandomPerson
的状态恰好是 在那一刻——即 rperson1
可能开始是 Kyle
,然后当 rperson2
被实例化时, rperson2
和 rperson1
都可以是 John
(或者它们都可以是 Kyle
,然后在创建 rperson3
时切换到 John
)。
不用说,这是非常奇怪的行为。事实上它太奇怪了,我怀疑你的设计需要彻底检修。我真的不认为有一个很好的理由去这样做(除了可能对某人开个恶作剧之外)。
如果您不想将此行为混入您的 Person
class,您也可以单独进行:
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
在你的
I'm gonna go with changing the class of the object, like you pointed out:
rand_person = random.choice((John, Kyle))
andself.__class__ = rand_person
. I've moved the methods ofRandomPerson
back intoPerson
, andRandomPerson
works now like a factory-ish generating class.
如果我可以这么说,这是一个奇怪的选择。首先,在对象创建后换出 class 似乎不是很 pythonic(它过于复杂)。最好随机生成对象实例,而不是事后分配 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
对象,为什么还要一个呢?只需使用工厂函数:
def RandomPerson():
return random.choice((Kyle,John))()