【问题标题】:Changing the class type of a class after inserted data插入数据后更改类的类类型
【发布时间】:2012-02-26 23:22:23
【问题描述】:

我想在 python 中创建一个类,它应该像这样工作:

  1. 数据已分配,可能绑定到变量(例如a = exampleclass(data) 或只是exampleclass(data)

  2. 在被插入数据时,应该自动判断数据的某些属性,如果某些属性满了,会自动...

  3. ...将类更改为另一个类

第 3 部分是我有问题的部分。我如何真正改变班级内部的班级?例如:

如果我有两个类,一个是Small_Numbers,一个是Big_numbers;现在我希望将任何小于 1000 的 small_number 转移到 Big_number 中,反之亦然,测试代码:

a = Small_number(50)
type(a) # should return Small_number.
b = Small_number(234234)
type(b) # should return Big_number.
c = Big_number(2)
type(c) # should return Small_number.

这样可以吗?

【问题讨论】:

  • 哦,谢谢,我现在查看了您所做的更改,很明显是需要的

标签: python class python-3.x


【解决方案1】:

为什么不使用工厂方法?这将根据传递的数据决定要实例化哪个类。使用您的示例:

def create_number(number):
    if number < 1000:
        return SmallNumber(number)
    return BigNumber(number)

【讨论】:

  • 是的,或者(SmallNumber if number &lt; 1000 else BigNumber)(number)
【解决方案2】:

不要。请改用工厂函数。

def create_number(source):
    if source < 1000:
       return Small_number(source)
    else:
       return Big_number(source)

a = create_number(50)
b = create_number(234234)
c = create_number(2)

【讨论】:

    【解决方案3】:

    使用factory method 是解决此问题的常用方法,尤其是,因为实例化类与调用 Python 中的函数没有区别。

    不过,如果你真的想要,你可以分配给self.__class__

    THRESHOLD = 1000
    
    class Small(object):
        def __init__(self, n):
            if n < THRESHOLD:
                self.n = n
            else:
                self.__class__ = Big
                self.__init__(n)
    
    class Big(object):
        def __init__(self, n):
            if n < THRESHOLD:
                self.__class__ = Small
                self.__init__(n)
            else:
                self.n = n
    

    这按预期工作:

    >>> a = Small(100)
    >>> type(a)
    <class 'Small'>
    >>> b = Small(1234)
    >>> type(b)
    <class 'Big'>
    >>> c = Big(2)
    >>> type(c)
    <class 'Small'>
    

    如果分配给self.__class__ 看起来太奇怪,那么改为you can override __new__。该方法在__init__被调用之前被调用,可以用来选择要实例化的类:

    THRESHOLD = 1000
    
    class Switcher(object):
        def __new__(cls, n):
            if n < THRESHOLD:
                new_cls = Small
            else:
                new_cls = Big
            instance = super(Switcher, new_cls).__new__(new_cls, n)
            if new_cls != cls:
                instance.__init__(n)
            return instance
    
    class Small(Switcher):
        def __init__(self, n):
            self.n = n
    
    class Big(Switcher):
        def __init__(self, n):
            self.n = n
    

    【讨论】:

    • 这种方法很酷,但它并不是 100% 有效:你会时不时得到“TypeError: __class__ assignment: 'X' object layout different from 'Y'” ,如果你的类基础没有经过仔细选择。
    • @RomanSusi:好点!我可以看到in the Python source,如果类使用不同的__slots__,您将收到此错误。手册的bottom of this section 也提到了这一点。
    • 我强烈建议 not 更改对象的类型(即,不要分配给 __class__)。请参阅this comment from the docs,并考虑特殊方法可能会在您执行此操作后无法预测地起作用。即使你认为你确切地知道会发生什么,你也可以接受,你可能会被下一版本的 Python 搞砸——毕竟,Python 没有明确承诺它将如何(或不会)工作。
    • @max:是的,涉及__class__分配的解决方案也是我最不喜欢的。
    • 覆盖 __new__ 的解决方案需要检查 new_cls 是否不是 cls (if not issubclass(client_cls, cls)) 的子类,或者执行除调用 __init__ 之外的其他操作,因为这会导致__init__ 子类被调用两次。您可以通过在每个子类的__init__ 中添加一个打印语句来测试这一点。
    猜你喜欢
    • 2018-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-23
    • 2018-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多