Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to understand lazy function in Django utils functional module

Tags:

python

django

I am learning from Django source code. When I read about functional module in Django, I don't know how to understand it. What the function is for and how to explain the implement of it.

This is my first to use stackoverflow. If some rules in here I didn't notice, please remind me.Thanks.

the code:

class Promise(object):
    """
    This is just a base class for the proxy class created in
    the closure of the lazy function. It can be used to recognize
    promises in code.
    """
    pass


def lazy(func, *resultclasses):
    """
    Turns any callable into a lazy evaluated callable. You need to give result
    classes or types -- at least one is needed so that the automatic forcing of
    the lazy evaluation code is triggered. Results are not memoized; the
    function is evaluated on every access.
    """

    @total_ordering
    class __proxy__(Promise):
        """
        Encapsulate a function call and act as a proxy for methods that are
        called on the result of that function. The function is not evaluated
        until one of the methods on the result is called.
        """
        __dispatch = None

        def __init__(self, args, kw):
            self.__args = args
            self.__kw = kw
            if self.__dispatch is None:
                self.__prepare_class__()

        def __reduce__(self):
            return (
                _lazy_proxy_unpickle,
                (func, self.__args, self.__kw) + resultclasses
            )

        @classmethod
        def __prepare_class__(cls):
            cls.__dispatch = {}
            for resultclass in resultclasses:
                cls.__dispatch[resultclass] = {}
                for type_ in reversed(resultclass.mro()):
                    for (k, v) in type_.__dict__.items():
                        # All __promise__ return the same wrapper method, but
                        # they also do setup, inserting the method into the
                        # dispatch dict.
                        meth = cls.__promise__(resultclass, k, v)
                        if hasattr(cls, k):
                            continue
                        setattr(cls, k, meth)
            cls._delegate_bytes = bytes in resultclasses
            cls._delegate_text = six.text_type in resultclasses
            assert not (cls._delegate_bytes and cls._delegate_text), "Cannot call lazy() with both bytes and text return types."
            if cls._delegate_text:
                if six.PY3:
                    cls.__str__ = cls.__text_cast
                else:
                    cls.__unicode__ = cls.__text_cast
            elif cls._delegate_bytes:
                if six.PY3:
                    cls.__bytes__ = cls.__bytes_cast
                else:
                    cls.__str__ = cls.__bytes_cast

        @classmethod
        def __promise__(cls, klass, funcname, method):
            # Builds a wrapper around some magic method and registers that
            # magic method for the given type and method name.
            def __wrapper__(self, *args, **kw):
                # Automatically triggers the evaluation of a lazy value and
                # applies the given magic method of the result type.
                res = func(*self.__args, **self.__kw)
                for t in type(res).mro():
                    if t in self.__dispatch:
                        return self.__dispatch[t][funcname](res, *args, **kw)
                raise TypeError("Lazy object returned unexpected type.")

            if klass not in cls.__dispatch:
                cls.__dispatch[klass] = {}
            cls.__dispatch[klass][funcname] = method
            return __wrapper__

        def __text_cast(self):
            return func(*self.__args, **self.__kw)

        def __bytes_cast(self):
            return bytes(func(*self.__args, **self.__kw))

        def __cast(self):
            if self._delegate_bytes:
                return self.__bytes_cast()
            elif self._delegate_text:
                return self.__text_cast()
            else:
                return func(*self.__args, **self.__kw)

        def __ne__(self, other):
            if isinstance(other, Promise):
                other = other.__cast()
            return self.__cast() != other

        def __eq__(self, other):
            if isinstance(other, Promise):
                other = other.__cast()
            return self.__cast() == other

        def __lt__(self, other):
            if isinstance(other, Promise):
                other = other.__cast()
            return self.__cast() < other

        def __hash__(self):
            return hash(self.__cast())

        def __mod__(self, rhs):
            if self._delegate_bytes and six.PY2:
                return bytes(self) % rhs
            elif self._delegate_text:
                return six.text_type(self) % rhs
            return self.__cast() % rhs

        def __deepcopy__(self, memo):
            # Instances of this class are effectively immutable. It's just a
            # collection of functions. So we don't need to do anything
            # complicated for copying.
            memo[id(self)] = self
            return self

    @wraps(func)
    def __wrapper__(*args, **kw):
        # Creates the proxy object, instead of the actual value.
        return __proxy__(args, kw)

    return __wrapper__
like image 581
versionzhang Avatar asked Feb 06 '15 02:02

versionzhang


People also ask

What is lazy Django?

Django querysets are said to be lazily loaded and cached¹ ². Lazy loading means that until you perform certain actions on the queryset, such as iterating over it, the corresponding DB query won't be made. Caching means that if you re-use the same queryset, multiple DB queries won't be made.

What is the use of utils PY in Django?

Django offers many utility functions (particularly in django. utils ) that take a string as their first argument and do something to that string. These functions are used by template filters as well as directly in other code.

What is SimpleLazyObject?

SimpleLazyObject , itself is a subclass of LazyObject . LazyObject is, as described by the actual code: A wrapper for another class that can be used to delay instantiation of the wrapped class.

What is Force_text in Django?

force_text is a callable within the django. utils. encoding module of the Django project.


1 Answers

This function takes function and any number of classes. If to simplify, it returns wrapper(lets say "lazy function") instead of that function. At that point we can say that we turned function into lazy function. After that we can call this lazy function. Once called, it will return instance of proxy class, without calling the initial function instead of result of initial function. The initial function will be called only after we invoke any method on that result(proxy instance). *resultclasses here is the classes, instances of which are expected as results of the initial function

For example:

def func(text):
    return text.title()

lazy_func = lazy(func, str)
#lazy functon. prepared to dispatch any method of str instance.

res = lazy_func('test') #instance of __proxy__ class instead of 'Test' string.

res.find('T') #only at that point we call the initial function

I'll try to explain how it works in overall:

def lazy(func, *resultclasses): #On decorate

    @total_ordering
    class __proxy__(Promise):
        __dispatch = None

        def __init__(self, args, kw): #On call
            #3) __proxy__ instance stores the original call's args and kwargs. args = ('Test', ) for our example
            self.__args = args
            self.__kw = kw
            if self.__dispatch is None:
                self.__prepare_class__()
            #4) if it's the first call ot lazy function, we should prepare __proxy__ class

            #On the first call of the __wrapper__ function we should prepare class. Class preparation in this case
            #means that we'll fill the __dispatch class attribute with links to all methods of each result class.
            #We need to prepare class only on first call.

        @classmethod
        def __prepare_class__(cls):
            cls.__dispatch = {}
            for resultclass in resultclasses:
                #5) Looping through the resultclasses. In our example it's only str
                cls.__dispatch[resultclass] = {}
                for type_ in reversed(resultclass.mro()):
                    #6) looping through each superclass of each resultclass in reversed direction.
                    # So that'll be (object, str) for our example
                    for (k, v) in type_.__dict__.items():
                        #7) Looping through each attribute of each superclass. For example k = 'find', v = str.find
                        meth = cls.__promise__(resultclass, k, v)
                        if hasattr(cls, k):
                            continue
                        setattr(cls, k, meth)
                        #9) If __proxy__ class doesn't have attribute 'find' for example, we set the __wrapper__ to
                        #that attribute
                        #So class __proxy__ will have the __wrapper__ method in  __proxy__.__dict__['find'].
                        #And so on for all methods.


        @classmethod
        def __promise__(cls, klass, funcname, method):
            # Builds a wrapper around some magic method and registers that
            # magic method for the given type and method name.
            def __wrapper__(self, *args, **kw): #При вызове каждого метода результирующего класса (str)
                # Automatically triggers the evaluation of a lazy value and
                # applies the given magic method of the result type.
                res = func(*self.__args, **self.__kw)
                #10 finally we call the original function
                for t in type(res).mro():
                    #11) We're looping through all the superclasses of result's class from the bottom to the top
                    #That''ll be (str, object) for our example
                    if t in self.__dispatch:
                        #12) If the class is dispatched we pass the result with args and kwargs to
                        #__proxy__.__dispatch[str]['find'] which is unbound method 'find' of str class
                        #For our example res = 'Test', args = ('T', )
                        return self.__dispatch[t][funcname](res, *args, **kw)
                raise TypeError("Lazy object returned unexpected type.")


            if klass not in cls.__dispatch:
                cls.__dispatch[klass] = {}
            cls.__dispatch[klass][funcname] = method
            #7) Adds __proxy__.__dispatch[str]['find'] = str.find for example which is unbound method 'find' of str class
            #and so on with each method of each superclass of each resultclass
            #8) Returns new __wrapper__ method for each method of each resultclass. This wrapper method has the
            #funcname variable in closure.

            return __wrapper__


    @wraps(func) #makes the lazy function look like the initial
    def __wrapper__(*args, **kw):
        # Creates the proxy object, instead of the actual value.
        return __proxy__(args, kw)
        #2)On call of lazy function we get  __proxy__ instance instead of the actual value


    return __wrapper__
    #1)As the result of lazy(func, *resultclasses) call we get the __wrapper__ function, which looks like
    #the initial function because of the @wraps decorator
like image 141
Alex Polekha Avatar answered Oct 13 '22 02:10

Alex Polekha