在类中重写__new__方法后,新对象创建不能正常工作

时间:2014-02-05 04:23:44

标签: python django

我有一个奇怪的问题,我无法理解。我正在尝试使用https://github.com/Climbcare/unleashed将一些订单推送到Unleashed API。它有以下Python生成器类,它由另一个类继承:

class MetaResource(type):
    def __new__(mcs, name, bases, dct):
        """
        Looks for attributes whith a `__resourcefield__`  attribute and adds them to  `        __resourcefields__`.
        Replace the attribute with a property so its value can be directly accessed.
        """

        dct['__resourcefields__'] = {}
        dct['__embeddedresources__'] = {}

        for attr_name, attr in dct.iteritems():
            if hasattr(attr, '__resourcefield__') and attr.__resourcefield__:
                dct['__resourcefields__'][attr_name] = attr
                attr.__fieldname__ = attr_name
                attr.__parentresource__ = name
            elif hasattr(attr, '__metaclass__') and attr.__metaclass__ == mcs:
                dct['__embeddedresources__'][attr_name] = attr

        return super(MetaResource, mcs).__new__(mcs, name, bases, dct)

    def __init__(cls, name, bases, dct):
        cls.guess_endpoint()
        cls.convert_fields()
        cls.convert_embedded_resources()
        super(MetaResource, cls).__init__(name, bases, dct)

该课程由此课程扩展:

class UnleashedResource(object):
    __metaclass__ = MetaResource

    # Override if necessary
    __endpoint__ = None

    # Created by metaclass
    __resourcefields__ = {}
    __embeddedresources__ = {}

    def __repr__(self):
        return json.dumps(
            self.to_dict(),
            sort_keys=True,
            indent=4,
            separators=(',', ': ')
        )

    def from_dict(self, dict_val):
        """
        Set all the resource's field values from a dictionary.
        """
        if not dict_val:
            return

        for field, value in dict_val.iteritems():
            if hasattr(self, field):
                setattr(self, field, value)

然后有一个扩展UnleashedResource类的类:

class Product(UnleashedResource):
    __endpoint__ = 'Products'

    AverageLandPrice = fields.FieldNullableDecimal()
    Barcode = fields.FieldString()
    BinLocation = fields.FieldString()
    CanAutoAssemble = fields.FieldBoolean()
    ...

正如你可以看到的,MetaResource是UnleashedResource类的元类

奇怪的是,当您创建Product()类的多个实例时,所有这些实例都是相同的,即使它们具有不同的内存ID。例如:

p1 = Product()
p2 = Product()

对p1的任何更改也将存在于p2中。而印刷产品()也会给我同样的东西。

我读了元类,我仍然无法到达任何地方。

检查出来:

>>> from unleashed.resources.product import Product
>>> p1 = Product()
>>> type(p1)
<class 'unleashed.resources.product.Product'>
>>> p1.Barcode = 12345
>>> p2 = Product()
>>> p2.Barcode
12345
>>> id(p1)
4414711632
>>> id(p2)
4414356560
>>> Product().Barcode
12345
>>> id(Product())
4414711568
>>> type(Product())
<class 'unleashed.resources.product.Product'>
>>> 

非常感谢任何帮助。

1 个答案:

答案 0 :(得分:1)

这绝对是一个棘手的问题,但我想我已经弄明白这里发生了什么。元类采用“字段”的所有类属性并将它们转换为属性。

现在通常,当您为实例分配属性时,它将始终是实例属性,即使还存在具有相同名称的class属性。但是,如果您的class属性是定义了setter的属性,则分配给该属性(无论是来自类还是实例)将调用setter函数。这里的最终结果是通过实例分配给一个类属性将导致为所有实例更改该属性。

最简单的想法是,分配给一个属性并不是一个真正的赋值,它是一个变异。在下面的代码中f1.foof2.foo是同一个对象并且修改另一个代码会修改另一个代码,这不应该太令人惊讶:

class Foo(object):
    test = []

f1 = Foo()
f2 = Foo()
f1.test.append(1)  # f1.test is actually Foo.test, so class attribute changes
print f2.test      # f2.test is also Foo.test, so this prints [1]

同样的原则适用于属性和描述符,例如:

class Test(object):
    def __init__(self, val=None):
        self.val = val

    def __get__(self, obj, type=None):
        return self.val

    def __set__(self, obj, val):
        self.val = val

class Foo(object):
    test = Test()

f1 = Foo()
f2 = Foo()
f1.test = 'abc'  # calls Foo.test.__set__
print f2.test    # prints 'abc', because Foo.test was modified above