python类工厂继承随机父级 [英] python class factory inherit random parent
问题描述
我有一些类似这样的代码:
I have some code like this:
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:
How could i implement RandomPerson, with the following requirements:
- 调用
person = RandomPerson()
必须返回RandomPerson
对象. -
RandomPerson
应该随机地将John
或Kyle
子类化.
- calling
person = RandomPerson()
must return aRandomPerson
object. RandomPerson
should subclass eitherJohn
orKyle
randomly.
推荐答案
在我的原始答案中(我删除了它是因为这完全是错误的),我说我会考虑这样做:
In my original answer (which I deleted because it was just plain wrong) I said I would consider doing it like this:
class RandomPerson(Person):
def __init__(self):
rand_person = random.choice((John, Kyle))()
self.__dict__ = rand_person.__dict__
这种方式是对Python 博格成语;这个想法是关于对象的所有重要内容都包含在其 __ dict __
中.
This way is an adaptation of the Python Borg idiom; the idea was that everything that matters about an object is contained in its __dict__
.
但是,这仅在覆盖相同类的对象时才有效(这是您在Borg习语中所做的事情);对象 __ dict __
仅包含与对象实例有关的状态信息,而不包含对象类.
However, this only works when overwriting objects of the same class (which is what you are doing in the Borg idiom); the object __dict__
only contains state information pertaining to object instance, not the object class.
可以像这样切换出对象的类:
It is possible to switch out the class of an object like so:
class RandomPerson(Person):
def __init__(self):
rand_person = random.choice((John, Kyle))
self.__class__ = rand_person
但是,以这种方式进行操作将意味着对 RandomPerson
的调用将不会根据您的要求返回 RandomPerson
的实例,而返回的是 Kyle code>或
John
.所以这是不行的.
However, doing it this way would mean that the call to RandomPerson
would then not return an instance of RandomPerson
per your requirement, but of Kyle
or of John
. So this is a no go.
这是一种获取起作用的 RandomPerson
对象的方式,例如 Kyle
或 John
,但是不是:
Here is a way to get a RandomPerson
object that acts like Kyle
or John
, but isn't:
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
类的大脑.不幸的是,没有迹象表明发生了这种情况:
This one - very similar to the Borg idiom, except doing it with classes instead of instance objects and we're only copying the current version of the chosen class dict - is really pretty evil: we have lobotomized the RandomPerson
class and (randomly) stuck the brains of a Kyle
or John
class in place. And there is no indication, unfortunately, that this happened:
>>> rperson = RandomPerson()
>>> assert isinstance(rperson,Kyle) or isinstance(rperson,John)
AssertionError
所以我们 still 并没有真正地将 Kyle
或 John
子类化.另外,这真的是非常邪恶的.因此,除非您有充分的理由,否则请不要这样做.
So we still haven't really subclassed Kyle
or John
. Also, this is really really evil. So please don't do it unless you have a really good reason.
现在,假设您确实有充分的理由,那么上面的解决方案应该足够好,如果您所要做的只是确保可以使用任何类状态信息(方法和类属性)来自 Kyle
或 John
和 RandomPerson
.但是,如前所述, RandomPerson
仍然不是两个的真正子类.
Now, assuming you do in fact have a good reason, the above solution should be good enough if all you are after is making sure you can use any class state information (methods and class attributes) from Kyle
or John
with RandomPerson
. However, as illustrated prior, RandomPerson
still isn't a true subclass of either.
据我所知,几乎没有办法在实例创建时实际上将对象的类随机子集化,并且无法让该类在多个实例创建时保持状态.您将不得不伪造它.
Near as I can tell there is no way to actually randomly subclass an object's class at instance creation AND to have the class maintain state across multiple instance creations. You're going to have to fake it.
一种伪造方法是允许 RandomPerson
使用 Kyle
的子类.://docs.python.org/2/library/abc.html"rel =" nofollow>抽象基类模块和 __ subclasshook __
,并将其添加到您的 Person
类.看来这将是一个很好的解决方案,因为 Person
类是一个接口,无论如何也不会直接使用.
One way to fake it is to allow RandomPerson
to be considered a subclass of John
and Kyle
using the abstract baseclass module and __subclasshook__
, and adding that to your Person
class. This looks like it will be a good solution since the Person
class is an interface and isn't going to be directly used, anyway.
这是一种实现方法:
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 的子类code>,并且还共享
Kyle
或 John
的状态.实际上,每次创建新实例时(或更改 RandomPerson.identity
时),它都会在两者之间来回随机切换.这样做的另一个效果是:如果您有多个 RandomPerson
实例,则它们所有 share 的状态恰好是 RandomPerson
的状态-那时候, rperson1
可能开始是 Kyle
,然后在实例化 rperson2
时,两个rperson2
和 rperson1
可以是 John
(或者它们都可以是 Kyle
,然后切换到 John
创建 rperson3
时).
Now RandomPerson
- though it technically is not a subclass - is considered to be a subclass of Kyle
or John
, and it also shares the state of Kyle
or John
. In fact, it will switch back and forth between the two, randomly, every time a new instance is created (or when RandomPerson.identity
is changed). Another effect of doing things this way: if you have multiple RandomPerson
instances, they all share the state of whatever RandomPerson
happens to be in that moment -- i.e., rperson1
might start out being Kyle
, and then when rperson2
is instantiated, both rperson2
AND rperson1
could be John
(or they could both be Kyle
and then switch to John
when rperson3
is created).
不用说,这是非常奇怪的行为.实际上,它是如此的怪异,我怀疑您的设计需要进行彻底的检修.我真的不认为永远没有这样做的充分理由(除了可能对某人开个恶作剧之外).
Needless to say, this is pretty weird behavior. In fact it is so weird, my suspicion is that your design needs a complete overhaul. I really don't think there is a very good reason to EVER do this (other than maybe playing a bad joke on someone).
如果您不想将此行为混入您的 Person
类中,也可以单独进行:
If you don't want to mix this behavior into your Person
class, you could also do it separately:
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类工厂继承随机父级的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!