The Magic method in Python is a deep understanding of _python

Source: Internet
Author: User
Tags static class unique id in python

Touch Python also has a period of time, Python related frameworks and modules have also contacted a lot, hoping to contact their own feel better than the design and realization of sharing to everyone, so took a "charming Python" small mark, is to give himself a head, I hope you will criticize me a lot. :)

From Flask Import Request

Flask is a very popular Python web framework, the author also wrote some big and small projects, flask has a feature I like very much, that is, no matter where, if you want to get the current request object, as long as simple:

Copy Code code as follows:

From Flask Import Request

# get content from current request
Request.args
Request.forms
Request.Cookies
... ...


Very simple to remember, and very friendly to use. However, the simple hidden implementation can be a little more complicated. Follow my article to see the Mysteries of it!

Two questions?

Before we look down, we'll ask you two questions:

Question one: request, it looks like a static class instance, why we can use an expression like Request.args directly to get the Args property of the current request without using the example:

Copy Code code as follows:

From flask Import Get_request

# Get current request
Request = Get_request ()
Get_request (). args


Such a way? How does flask respond to the request to the current object?

Question two: In a real production environment, there may be a number of threads (or a coprocessor) under the same worker process, as I have just said, how does the instance of request work in such an environment?

To know the secret, we can only start from the source of flask to see.

Source code, source code, or source code

First we open Flask source code, from the beginning of __init__.py to see how the request came out:

Copy Code code as follows:

# file:flask/__init__.py
From. Globals import Current_app, G, request, session, _request_ctx_stack


# file:flask/globals.py
From Functools Import Partial
From werkzeug.local import Localstack, Localproxy


def _lookup_req_object (name):
top = _request_ctx_stack.top
If Top is None:
Raise RuntimeError (' working outside of Request context ')
Return GetAttr (top, name)

# context Locals
_request_ctx_stack = Localstack ()
Request = Localproxy (partial (_lookup_req_object, ' request '))

We can see that flask's request was introduced from globals.py, and here the code for the request is defined as Request = Localproxy (partial (_lookup_req_object, ' request '). If there is no understanding of what partial is the students need to make up the class, first need to understand the partial.

But we can simply understand that partial (func, ' request ') uses ' request ' as the first default parameter of Func to produce another function.

So, partial (_lookup_req_object, ' request ') we can understand that:

Generates a callable function that essentially obtains the first RequestContext object on the top of the stack from _request_ctx_stack this Localstack object. The request property of this object is then returned.

The localproxy of this werkzeug has caught our attention, let's see what it is:

Copy Code code as follows:

@implements_bool
Class Localproxy (object):
"" "Acts as a proxy for a Werkzeug the local. Forwards all operations to
A Proxied object. The only operations isn't supported for forwarding
are right handed operands and any kind of assignment.
... ...

Look at the first few sentences to know what it is mainly to do, as the name suggests, Localproxy is mainly on a proxy, a Werkzeug for the local object service agent. He "forwards" all his actions to the objects it acts on.

So how does this proxy get through python? The answer is in the source code:

Copy Code code as follows:

# to make it easier to explain, I've made some cuts and changes to the code.

@implements_bool
Class Localproxy (object):
__slots__ = (' __local ', ' __dict__ ', ' __name__ ')

def __init__ (self, Local, Name=none):
# Here's a point to note, through the __setattr__ method, Self's
# "_localproxy__local" attribute is set to local, you may be curious
# Why is this attribute name so strange, it's actually because Python doesn't support real
# Private member, please refer to official documentation:
# http://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references
# Here you can just think of it as Self.__local = local:)
Object.__setattr__ (self, ' _localproxy__local ', local)
Object.__setattr__ (self, ' __name__ ', name)

def _get_current_object (self):
"""
Gets the real object that is currently being represented, and typically does not call this method voluntarily unless you
Some performance reasons need to be acquired to do the real object of the proxy, or you need to use it for another
Place.
"""
# here is mainly to judge the object of the agent is not a Werkzeug local object, in our analysis request
# in the process, it will not use this piece of logic.
If not hasattr (self.__local, ' __release_local__ '):
# from Localproxy (partial (_lookup_req_object, ' request '))
# by calling the Self.__local () method, we get the partial (_lookup_req_object, ' request ') ()
# is the ' _request_ctx_stack.top.request '
Return self.__local ()
Try
Return GetAttr (self.__local, self.__name__)
Except Attributeerror:
Raise RuntimeError (' no object bound to%s '% self.__name__)

# and then there's a big section of Python magic method, the local proxy overload (almost)? All Python
# built-in Magic method, so that all about his own operations are pointed to the _get_current_object ()
# The object returned, which is the true proxy object.

... ...
__setattr__ = lambda x, N, V:setattr (X._get_current_object (), N, v)
__delattr__ = lambda x, n:delattr (X._get_current_object (), N)
__str__ = Lambda x:str (x._get_current_object ())
__lt__ = lambda x, O:x._get_current_object () < O
__le__ = lambda x, o:x._get_current_object () <= o
__eq__ = lambda x, o:x._get_current_object () = O
__ne__ = lambda x, o:x._get_current_object ()!= o
__gt__ = lambda x, O:x._get_current_object () > O
__ge__ = lambda x, o:x._get_current_object () >= o
... ...

The second question we had at the beginning of the article was answered, and it was localproxy that we did not need to use the Get_request () method call to get the current request object.

Localproxy as an agent by customizing the Magic method. Represented all of our actions for the request, and directed it to the real request object.

Well, now I know Request.args is not as simple as it seems.

Now, let's take a look at the second question: How does the request work in a multi-threaded environment? Let's go back to globals.py:

Copy Code code as follows:

From Functools Import Partial
From werkzeug.local import Localstack, Localproxy


def _lookup_req_object (name):
top = _request_ctx_stack.top
If Top is None:
Raise RuntimeError (' working outside of Request context ')
Return GetAttr (top, name)

# context Locals
_request_ctx_stack = Localstack ()
Request = Localproxy (partial (_lookup_req_object, ' request '))

The crux of the problem lies in this _request_ctx_stack object, let us find the source code of Localstack:

Copy Code code as follows:

Class Localstack (object):

def __init__ (self):
# In fact, Localstack mainly used another local class
# Some of its key methods are also being represented on this local class
# compared to the local class, it implements some of the stack-related methods, such as push, pop, etc.
# so we can just look at the local code directly
self._local = local ()

... ...

@property
def top (self):
"""
Returns the object at the top of the stack
"""
Try
return Self._local.stack[-1]
Except (Attributeerror, Indexerror):
Return None


# So when we call _request_ctx_stack.top, it's actually called _request_ctx_stack._local.stack[-1]
# Let's take a look at how the local class is implemented, but before we do, we'll have to look at the Get_ident method that appears below.

# first try to import the GetCurrent method from Greenlet, because if flask is running under a container like gevent
# So requests are based on Greenlet as the smallest unit, not the thread thread.
Try
From Greenlet import GetCurrent as Get_ident
Except Importerror:
Try
From thread import get_ident
Except Importerror:
From _thread import get_ident

# in short, this get_ident method will return the current coprocessor/thread ID, which is unique for each request


Class local (object):
__slots__ = (' __storage__ ', ' __ident_func__ ')

def __init__ (self):
Object.__setattr__ (self, ' __storage__ ', {})
Object.__setattr__ (self, ' __ident_func__ ', get_ident)

... ...

# The key to the problem is that the local class overloads the two magical methods of __getattr__ and __setattr__

def __getattr__ (self, name):
Try
# Here we return the call to Self.__ident_func__ (), which is the current unique ID
# as the key to __storage__
Return self.__storage__[self.__ident_func__ ()][name]
Except Keyerror:
Raise Attributeerror (name)

def __setattr__ (self, Name, value):
Ident = self.__ident_func__ ()
Storage = self.__storage__
Try
Storage[ident][name] = value
Except Keyerror:
Storage[ident] = {Name:value}

... ...

# After the two magic methods are overloaded

# local (). Some_value is no longer what it seems so simple:
# First we call the Get_ident method to get the currently running thread/coprocessor ID
# then get the Some_value attribute under this ID space, just like this:
#
# local ()-some_value-> Local () [Current_thread_id ()].some_value
#
# It's also true when setting properties

Through these analyses, I believe that question two has also been resolved by using the current thread/coprocessor ID, and by overloading some magical methods, flask implements a stack object that allows different worker threads to use their own. This guarantees the normal work of the request.

Speaking of which, this article is about the same. We can see that for the convenience of the users, developers as frameworks and tools need to do a lot of extra work, and sometimes it's unavoidable to use some language magic, and Python has a pretty good support for that.

All we need to do is learn to master the magical parts of Python and use magic to make your code simpler and easier to use.

But keep in mind that magic, while dazzling, must not be abused oh.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.