python类工厂继承随机父级 [英] python class factory inherit random parent

查看:54
本文介绍了python类工厂继承随机父级的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些类似这样的代码:

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 a RandomPerson object.
  • RandomPerson should subclass either John or Kyle 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 使用John 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆