Fixed #3438: improved the speed of Model.__init__ (about 1/3 faster, it appears). Thanks (a lot!) to Brian Harring.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@4597 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jacob Kaplan-Moss 2007-02-26 06:16:19 +00:00
parent abf79841fe
commit 90acc8ff7e
1 changed files with 61 additions and 30 deletions

View File

@ -13,6 +13,7 @@ from django.dispatch import dispatcher
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
from django.utils.functional import curry from django.utils.functional import curry
from django.conf import settings from django.conf import settings
from itertools import izip
import types import types
import sys import sys
import os import os
@ -90,31 +91,63 @@ class Model(object):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
dispatcher.send(signal=signals.pre_init, sender=self.__class__, args=args, kwargs=kwargs) dispatcher.send(signal=signals.pre_init, sender=self.__class__, args=args, kwargs=kwargs)
for f in self._meta.fields: # there is a rather weird disparity here; if kwargs, it's set, then args overrides it.
if isinstance(f.rel, ManyToOneRel): # should be one or the other, don't duplicate the work
# the reason for the kwargs check is that standard iterator passes in by args, literally,
# the row- with this check, instantiation for iteration is 33% faster.
args_len = len(args)
if args_len > len(self._meta.fields):
# daft, but matches old exception sans the err msg.
raise IndexError("number of args exceeds number of fields")
fields_iter = iter(self._meta.fields)
if not kwargs:
# ordering of the izip calls matter- izip throws StopIteration when an iter throws it
# meaning, if the first iter throws it, the second is *not* consumed from
# we rely on this, thus don't change the order without changing the logic.
for val, field in izip(args, fields_iter):
setattr(self, field.attname, val)
else:
# slower...
for val, field in izip(args, fields_iter):
setattr(self, field.attname, val)
kwargs.pop(field.name, None)
# maintain compatibility with existing calls, daft as it is.
if isinstance(field.rel, ManyToOneRel):
kwargs.pop(field.attname, None)
# now we're left with the unprocessed fields that *must* come from keywords, or default.
for field in fields_iter:
if kwargs:
if isinstance(field.rel, ManyToOneRel):
try: try:
# Assume object instance was passed in. # Assume object instance was passed in.
rel_obj = kwargs.pop(f.name) rel_obj = kwargs.pop(field.name)
except KeyError: except KeyError:
try: try:
# Object instance wasn't passed in -- must be an ID. # Object instance wasn't passed in -- must be an ID.
val = kwargs.pop(f.attname) val = kwargs.pop(field.attname)
except KeyError: except KeyError:
val = f.get_default() val = field.get_default()
else: else:
# Object instance was passed in. # Object instance was passed in.
# Special case: You can pass in "None" for related objects if it's allowed. # Special case: You can pass in "None" for related objects if it's allowed.
if rel_obj is None and f.null: if rel_obj is None and field.null:
val = None val = None
else: else:
try: try:
val = getattr(rel_obj, f.rel.get_related_field().attname) val = getattr(rel_obj, field.rel.get_related_field().attname)
except AttributeError: except AttributeError:
raise TypeError, "Invalid value: %r should be a %s instance, not a %s" % (f.name, f.rel.to, type(rel_obj)) raise TypeError("Invalid value: %r should be a %s instance, not a %s" %
setattr(self, f.attname, val) (field.name, field.rel.to, type(rel_obj)))
else: else:
val = kwargs.pop(f.attname, f.get_default()) val = kwargs.pop(field.attname, field.get_default())
setattr(self, f.attname, val) else:
val = field.get_default()
setattr(self, field.attname, val)
if kwargs:
for prop in kwargs.keys(): for prop in kwargs.keys():
try: try:
if isinstance(getattr(self.__class__, prop), property): if isinstance(getattr(self.__class__, prop), property):
@ -123,8 +156,6 @@ class Model(object):
pass pass
if kwargs: if kwargs:
raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0] raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0]
for i, arg in enumerate(args):
setattr(self, self._meta.fields[i].attname, arg)
dispatcher.send(signal=signals.post_init, sender=self.__class__, instance=self) dispatcher.send(signal=signals.post_init, sender=self.__class__, instance=self)
def add_to_class(cls, name, value): def add_to_class(cls, name, value):