前言

基于flask 2.0.2

Local其实来源于ThreadLocal。ThreadLocal可以简单理解为用来管理不同线程中具有同一变量名、同一逻辑、但有不同具体内容的数据,使得数据只在当前线程有效。这是直观表现以及通常用法;更准确来说是管理不同线程的数据,实现thread-safe和thread-specific

这么说的原因是,我们写代码时通常只起一个变量名,但它们指代很多不同具体的个体,类似于我们起的名是一个类,代码运行时处理的是这个类的多个实例。例如A家有小孩代号为X, B家有小孩代号也为X,我们在获取它们家小孩名字时都访问了X,但X是两个不同的人。

flask的request就是基于上面的思想,后台接收到的请求都是叫request,但是是不同用户发出的request(相当于上面例子中的不同家庭的X), 即不同上下文中request具体内容不同

werkzeug实现自己Local的原因

  1. flask需要处理线程和协程(如gevent)两种情况,而threading.ThreadLocal()只支持线程这一种情景
  2. 单纯的threading.ThreadLoacl()不能满足web框架的需求

contextvar的出现是因为协程的原因。其用来管理不同协程之间的数据,可以类比上面的ThreadLocal。且contextvars是用来取代ThreadLocal的,官方文档中描述:

在多并发环境中,有状态上下文管理器应该使用上下文变量,而不是 threading.local() 来防止他们的状态意外泄露到其他代码。

werkzeug.local.py包含内容

  1. function: get_ident()
  2. class: Contextvar
  3. function: release_local()
  4. class: Local()
  5. class: LocalStack()
  6. class: LocalManager()
  7. class: _ProxyLookup()
  8. class: _ProxyIOp()
  9. function: _l_to_r_op()
  10. class: LocalProxy()

1. function: get_ident()

返回当前线程的标识,这个标识是唯一的,这意味着我们能唯一找到目标线程。

若使用了greenlet,则get_ident()实则为greenlet.getcurrent()
否则为threading.get_ident()

这个函数将在werkzeug 2.1移除

2. Class: Contextvar

python3.7 加入了contextvars.ContextVar即上下文变量,用于管理不同协程的数据。

local.py中处理ContextVar的逻辑为:

  1. 优先使用contextvars.ContextVar
  2. 检查是否使用greenletevenlet,且判断是否给contextvar打补丁
  3. 若不能使用contextvars.ContextVar或者没有打好补丁,那么使用flask实现的一个简单的ContextVar,其含有一个storage字典,用于存储键值对:key=上下文标识, value=上下文字典,上下文标识在线程时为线程id,为协程时为协程id;上下文字典则用来存储对应上下文的数据,通过上下文标识获得该字典。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    class ContextVar:  # type: ignore
        """A fake ContextVar based on the previous greenlet/threading
        ident function. Used on Python 3.6, eventlet, and old versions
        of gevent.
        """

        def __init__(self, _name: str) -> None:
            self.storage: t.Dict[int, t.Dict[str, t.Any]] = {}

        def get(self, default: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]:
            return self.storage.get(_get_ident(), default)

        def set(self, value: t.Dict[str, t.Any]) -> None:
            self.storage[_get_ident()] = value
  1. 注意get(), set()的用法,get({})表示,若上下文变量取不到值,则返回default{}

3. function: release_local()

Local()或者LocalStack()清楚当前上下文的数据,实际上是调用了Local()LocalStack()各自的__release_local__()方法, 请继续阅读下文的Local()LocalStack()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
def release_local(local: t.Union["Local", "LocalStack"]) -> None:
    """Releases the contents of the local for the current context.
    This makes it possible to use locals without a manager.

    Example::

        >>> loc = Local()
        >>> loc.foo = 42
        >>> release_local(loc)
        >>> hasattr(loc, 'foo')
        False

    With this function one can release :class:`Local` objects as well
    as :class:`LocalStack` objects.  However it is not possible to
    release data held by proxies that way, one always has to retain
    a reference to the underlying local object in order to be able
    to release it.

    .. versionadded:: 0.6.1
    """
    local.__release_local__()

4. class: Local()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
class Local:
    __slots__ = ("_storage",)

    def __init__(self) -> None:
        object.__setattr__(self, "_storage", ContextVar("local_storage"))

    @property
    def __storage__(self) -> t.Dict[str, t.Any]:
        warnings.warn(
            "'__storage__' is deprecated and will be removed in Werkzeug 2.1.",
            DeprecationWarning,
            stacklevel=2,
        )
        return self._storage.get({})  # type: ignore

    @property
    def __ident_func__(self) -> t.Callable[[], int]:
        warnings.warn(
            "'__ident_func__' is deprecated and will be removed in"
            " Werkzeug 2.1. It should not be used in Python 3.7+.",
            DeprecationWarning,
            stacklevel=2,
        )
        return _get_ident  # type: ignore

    @__ident_func__.setter
    def __ident_func__(self, func: t.Callable[[], int]) -> None:
        warnings.warn(
            "'__ident_func__' is deprecated and will be removed in"
            " Werkzeug 2.1. Setting it no longer has any effect.",
            DeprecationWarning,
            stacklevel=2,
        )

    def __iter__(self) -> t.Iterator[t.Tuple[int, t.Any]]:
        return iter(self._storage.get({}).items())

    def __call__(self, proxy: str) -> "LocalProxy":
        """Create a proxy for a name."""
        return LocalProxy(self, proxy)

    def __release_local__(self) -> None:
        self._storage.set({})

    def __getattr__(self, name: str) -> t.Any:
        values = self._storage.get({})
        try:
            return values[name]
        except KeyError:
            raise AttributeError(name)

    def __setattr__(self, name: str, value: t.Any) -> None:
        values = self._storage.get({}).copy()
        values[name] = value
        self._storage.set(values)

    def __delattr__(self, name: str) -> None:
        values = self._storage.get({}).copy()
        try:
            del values[name]
            self._storage.set(values)
        except KeyError:
            raise AttributeError(name)

  1. __storage__将被移除,改名为符合变量命名约定的_storage
  2. _storage是一个ContextVar,即上下文变量,对_storageset()get()操作,是对对应上下文变量键值对进行操作。
  3. __ident_func__即为上文提到的get_ident()函数,用于获取当前线程的id
  4. 实现了迭代器协议,是对_storage.items()的迭代(即对_storage字典键值对的迭代)
  5. 实现了__call__(self, proxy), 新实例化一个LocalProxy(local=local对象, proxy=上下文变量名)对象, 例如:
1
2
loc = Local()
req = loc("request")

其中req一个LocalProxy实例,构造参数为loc实例和变量名"request" 5. __release_local__是用来释放当前上下文(线程或协程)字典的内容,重新设置为空字典{}。参考上文的ContextVar().set()方法 6. __getattr__(), __setattr__(), __delattr__都是获取当前上下文字典,然后对字典内容进行操作

5. class: LocalStack

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
class LocalStack:
    """This class works similar to a :class:`Local` but keeps a stack
    of objects instead.  This is best explained with an example::

        >>> ls = LocalStack()
        >>> ls.push(42)
        >>> ls.top
        42
        >>> ls.push(23)
        >>> ls.top
        23
        >>> ls.pop()
        23
        >>> ls.top
        42

    They can be force released by using a :class:`LocalManager` or with
    the :func:`release_local` function but the correct way is to pop the
    item from the stack after using.  When the stack is empty it will
    no longer be bound to the current context (and as such released).

    By calling the stack without arguments it returns a proxy that resolves to
    the topmost item on the stack.

    .. versionadded:: 0.6.1
    """

    def __init__(self) -> None:
        self._local = Local()

    def __release_local__(self) -> None:
        self._local.__release_local__()

    @property
    def __ident_func__(self) -> t.Callable[[], int]:
        return self._local.__ident_func__

    @__ident_func__.setter
    def __ident_func__(self, value: t.Callable[[], int]) -> None:
        object.__setattr__(self._local, "__ident_func__", value)

    def __call__(self) -> "LocalProxy":
        def _lookup() -> t.Any:
            rv = self.top
            if rv is None:
                raise RuntimeError("object unbound")
            return rv

        return LocalProxy(_lookup)

    def push(self, obj: t.Any) -> t.List[t.Any]:
        """Pushes a new item to the stack"""
        rv = getattr(self._local, "stack", []).copy()
        rv.append(obj)
        self._local.stack = rv
        return rv  # type: ignore

    def pop(self) -> t.Any:
        """Removes the topmost item from the stack, will return the
        old value or `None` if the stack was already empty.
        """
        stack = getattr(self._local, "stack", None)
        if stack is None:
            return None
        elif len(stack) == 1:
            release_local(self._local)
            return stack[-1]
        else:
            return stack.pop()

    @property
    def top(self) -> t.Any:
        """The topmost item on the stack.  If the stack is empty,
        `None` is returned.
        """
        try:
            return self._local.stack[-1]
        except (AttributeError, IndexError):
            return None
  1. LocalStack是在Local的基础上实现的一个栈,栈被存在上下文变量self._local中,基于上文提到Local的原理,即通过上下文标识符获取当前上下文字典,而栈则存在key为"stack"对应的value中。stack实际上是一个list。
  2. pop()时,若只有一个元素,则返回这个元素,并将整个self._local释放(调用release_local())
  3. __call__()内有一个闭包函数,其作为LocalProxy的构造参数, __call__逻辑为对LocalStack()的无参数调用时,将用栈顶元素生成一个LocalProxy

6. class: LocalManager()

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
class LocalManager:
    """Local objects cannot manage themselves. For that you need a local
    manager. You can pass a local manager multiple locals or add them
    later y appending them to `manager.locals`. Every time the manager
    cleans up, it will clean up all the data left in the locals for this
    context.

    .. versionchanged:: 2.0
        ``ident_func`` is deprecated and will be removed in Werkzeug
         2.1.

    .. versionchanged:: 0.6.1
        The :func:`release_local` function can be used instead of a
        manager.

    .. versionchanged:: 0.7
        The ``ident_func`` parameter was added.
    """

    def __init__(
        self,
        locals: t.Optional[t.Iterable[t.Union[Local, LocalStack]]] = None,
        ident_func: None = None,
    ) -> None:
        if locals is None:
            self.locals = []
        elif isinstance(locals, Local):
            self.locals = [locals]
        else:
            self.locals = list(locals)

        if ident_func is not None:
            warnings.warn(
                "'ident_func' is deprecated and will be removed in"
                " Werkzeug 2.1. Setting it no longer has any effect.",
                DeprecationWarning,
                stacklevel=2,
            )

    @property
    def ident_func(self) -> t.Callable[[], int]:
        warnings.warn(
            "'ident_func' is deprecated and will be removed in Werkzeug 2.1.",
            DeprecationWarning,
            stacklevel=2,
        )
        return _get_ident  # type: ignore

    @ident_func.setter
    def ident_func(self, func: t.Callable[[], int]) -> None:
        warnings.warn(
            "'ident_func' is deprecated and will be removedin Werkzeug"
            " 2.1. Setting it no longer has any effect.",
            DeprecationWarning,
            stacklevel=2,
        )

    def get_ident(self) -> int:
        """Return the context identifier the local objects use internally for
        this context.  You cannot override this method to change the behavior
        but use it to link other context local objects (such as SQLAlchemy's
        scoped sessions) to the Werkzeug locals.

        .. deprecated:: 2.0
            Will be removed in Werkzeug 2.1.

        .. versionchanged:: 0.7
           You can pass a different ident function to the local manager that
           will then be propagated to all the locals passed to the
           constructor.
        """
        warnings.warn(
            "'get_ident' is deprecated and will be removed in Werkzeug 2.1.",
            DeprecationWarning,
            stacklevel=2,
        )
        return self.ident_func()

    def cleanup(self) -> None:
        """Manually clean up the data in the locals for this context.  Call
        this at the end of the request or use `make_middleware()`.
        """
        for local in self.locals:
            release_local(local)

    def make_middleware(self, app: "WSGIApplication") -> "WSGIApplication":
        """Wrap a WSGI application so that cleaning up happens after
        request end.
        """

        def application(
            environ: "WSGIEnvironment", start_response: "StartResponse"
        ) -> t.Iterable[bytes]:
            return ClosingIterator(app(environ, start_response), self.cleanup)

        return application

    def middleware(self, func: "WSGIApplication") -> "WSGIApplication":
        """Like `make_middleware` but for decorating functions.

        Example usage::

            @manager.middleware
            def application(environ, start_response):
                ...

        The difference to `make_middleware` is that the function passed
        will have all the arguments copied from the inner application
        (name, docstring, module).
        """
        return update_wrapper(self.make_middleware(func), func)

    def __repr__(self) -> str:
        return f"<{type(self).__name__} storages: {len(self.locals)}>"
  1. 构造参数locals为一个可迭代对象,其元素为Local或者LocalStack
  2. cleanup()locals中的每一个Local或者LocalStack调用release_local()
  3. make_middleware()WSGIApplication添加各种关闭动作,如关闭session和清空Local
  4. middleware()用于装饰器,功能类似于make_middleware

7. class: _ProxyLookup()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
class _ProxyLookup:
    """Descriptor that handles proxied attribute lookup for
    :class:`LocalProxy`.

    :param f: The built-in function this attribute is accessed through.
        Instead of looking up the special method, the function call
        is redone on the object.
    :param fallback: Call this method if the proxy is unbound instead of
        raising a :exc:`RuntimeError`.
    :param class_value: Value to return when accessed from the class.
        Used for ``__doc__`` so building docs still works.
    """

    __slots__ = ("bind_f", "fallback", "class_value", "name")

    def __init__(
        self,
        f: t.Optional[t.Callable] = None,
        fallback: t.Optional[t.Callable] = None,
        class_value: t.Optional[t.Any] = None,
    ) -> None:
        bind_f: t.Optional[t.Callable[["LocalProxy", t.Any], t.Callable]]

        if hasattr(f, "__get__"):
            # A Python function, can be turned into a bound method.

            def bind_f(instance: "LocalProxy", obj: t.Any) -> t.Callable:
                return f.__get__(obj, type(obj))  # type: ignore

        elif f is not None:
            # A C function, use partial to bind the first argument.

            def bind_f(instance: "LocalProxy", obj: t.Any) -> t.Callable:
                return partial(f, obj)  # type: ignore

        else:
            # Use getattr, which will produce a bound method.
            bind_f = None

        self.bind_f = bind_f
        self.fallback = fallback
        self.class_value = class_value

    def __set_name__(self, owner: "LocalProxy", name: str) -> None:
        self.name = name

    def __get__(self, instance: "LocalProxy", owner: t.Optional[type] = None) -> t.Any:
        if instance is None:
            if self.class_value is not None:
                return self.class_value

            return self

        try:
            obj = instance._get_current_object()
        except RuntimeError:
            if self.fallback is None:
                raise

            return self.fallback.__get__(instance, owner)  # type: ignore

        if self.bind_f is not None:
            return self.bind_f(instance, obj)

        return getattr(obj, self.name)

    def __repr__(self) -> str:
        return f"proxy {self.name}"

    def __call__(self, instance: "LocalProxy", *args: t.Any, **kwargs: t.Any) -> t.Any:
        """Support calling unbound methods from the class. For example,
        this happens with ``copy.copy``, which does
        ``type(x).__copy__(x)``. ``type(x)`` can't be proxied, so it
        returns the proxy type and descriptor.
        """
        return self.__get__(instance, type(instance))(*args, **kwargs)
  1. _ProxyLookup()是一个描述器(descriptor), 用于实现LocalProxy的property。描述器内容请取官方文档阅读那篇详细的文档。描述器相当于一个中间人,对LocalProxy属性的访问都先经过描述器。参考flask老版本可以发现以前并没有显式使用描述器,而是使用了一堆lambda函数,现在将使用lambda函数的共同逻辑抽象出来,实现了_ProxyLookup()。这是使用_ProxyLookup的原因
  2. __init__(f, fallback, class_val)中的逻辑:先判断f是否有__get__(), 若有,则在当前逻辑下可以判定其为函数,并将其与目标obj进行绑定。绑定并不是在当下进行,而是调用时进行(当LocalProxy访问用_ProxyLookup实现的property时,即调用__get__时)。所以返回一个执行绑定的函数bind_f
  3. __get__(instance, owner):LocalProxy中所有使用_ProxyLookup实现的property,每次访问它们就会调用对应的__get__。在__get__中执行对函数的绑定。

8. class: _ProxyIOp()

继承了_ProxyLookup, 与_ProxyLookup类似,用于实现LocalProxy的一系列property,具体的是用来实现一系列i_operate操作:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
__iadd__ = _ProxyIOp(operator.iadd)
__isub__ = _ProxyIOp(operator.isub)
__imul__ = _ProxyIOp(operator.imul)
__imatmul__ = _ProxyIOp(operator.imatmul)
__itruediv__ = _ProxyIOp(operator.itruediv)
__ifloordiv__ = _ProxyIOp(operator.ifloordiv)
__imod__ = _ProxyIOp(operator.imod)
__ipow__ = _ProxyIOp(operator.ipow)
__ilshift__ = _ProxyIOp(operator.ilshift)
__irshift__ = _ProxyIOp(operator.irshift)
__iand__ = _ProxyIOp(operator.iand)
__ixor__ = _ProxyIOp(operator.ixor)
__ior__ = _ProxyIOp(operator.ior)

9. function: _l_to_r_op()

将左操作转换为右操作:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
__radd__ = _ProxyLookup(_l_to_r_op(operator.add))
__rsub__ = _ProxyLookup(_l_to_r_op(operator.sub))
__rmul__ = _ProxyLookup(_l_to_r_op(operator.mul))
__rmatmul__ = _ProxyLookup(_l_to_r_op(operator.matmul))
__rtruediv__ = _ProxyLookup(_l_to_r_op(operator.truediv))
__rfloordiv__ = _ProxyLookup(_l_to_r_op(operator.floordiv))
__rmod__ = _ProxyLookup(_l_to_r_op(operator.mod))
__rdivmod__ = _ProxyLookup(_l_to_r_op(divmod))
__rpow__ = _ProxyLookup(_l_to_r_op(pow))
__rlshift__ = _ProxyLookup(_l_to_r_op(operator.lshift))
__rrshift__ = _ProxyLookup(_l_to_r_op(operator.rshift))
__rand__ = _ProxyLookup(_l_to_r_op(operator.and_))
__rxor__ = _ProxyLookup(_l_to_r_op(operator.xor))
__ror__ = _ProxyLookup(_l_to_r_op(operator.or_))

10. class: LocalProxy()

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
class LocalProxy:
    """A proxy to the object bound to a :class:`Local`. All operations
    on the proxy are forwarded to the bound object. If no object is
    bound, a :exc:`RuntimeError` is raised.

    .. code-block:: python

        from werkzeug.local import Local
        l = Local()

        # a proxy to whatever l.user is set to
        user = l("user")

        from werkzeug.local import LocalStack
        _request_stack = LocalStack()

        # a proxy to _request_stack.top
        request = _request_stack()

        # a proxy to the session attribute of the request proxy
        session = LocalProxy(lambda: request.session)

    ``__repr__`` and ``__class__`` are forwarded, so ``repr(x)`` and
    ``isinstance(x, cls)`` will look like the proxied object. Use
    ``issubclass(type(x), LocalProxy)`` to check if an object is a
    proxy.

    .. code-block:: python

        repr(user)  # <User admin>
        isinstance(user, User)  # True
        issubclass(type(user), LocalProxy)  # True

    :param local: The :class:`Local` or callable that provides the
        proxied object.
    :param name: The attribute name to look up on a :class:`Local`. Not
        used if a callable is given.

    .. versionchanged:: 2.0
        Updated proxied attributes and methods to reflect the current
        data model.

    .. versionchanged:: 0.6.1
        The class can be instantiated with a callable.
    """

    __slots__ = ("__local", "__name", "__wrapped__")

    def __init__(
        self,
        local: t.Union["Local", t.Callable[[], t.Any]],
        name: t.Optional[str] = None,
    ) -> None:
        object.__setattr__(self, "_LocalProxy__local", local)
        object.__setattr__(self, "_LocalProxy__name", name)

        if callable(local) and not hasattr(local, "__release_local__"):
            # "local" is a callable that is not an instance of Local or
            # LocalManager: mark it as a wrapped function.
            object.__setattr__(self, "__wrapped__", local)

    def _get_current_object(self) -> t.Any:
        """Return the current object.  This is useful if you want the real
        object behind the proxy at a time for performance reasons or because
        you want to pass the object into a different context.
        """
        if not hasattr(self.__local, "__release_local__"):  # type: ignore
            return self.__local()  # type: ignore

        try:
            return getattr(self.__local, self.__name)  # type: ignore
        except AttributeError:
            raise RuntimeError(f"no object bound to {self.__name}")  # type: ignore

    __doc__ = _ProxyLookup(  # type: ignore
        class_value=__doc__, fallback=lambda self: type(self).__doc__
    )
    # __del__ should only delete the proxy
    __repr__ = _ProxyLookup(  # type: ignore
        repr, fallback=lambda self: f"<{type(self).__name__} unbound>"
    )
    __str__ = _ProxyLookup(str)  # type: ignore
    __bytes__ = _ProxyLookup(bytes)
    __format__ = _ProxyLookup()  # type: ignore
    __hash__ = _ProxyLookup(hash)  # type: ignore
    __bool__ = _ProxyLookup(bool, fallback=lambda self: False)
    __getattr__ = _ProxyLookup(getattr)
    # __getattribute__ triggered through __getattr__
    __setattr__ = _ProxyLookup(setattr)  # type: ignore
    __delattr__ = _ProxyLookup(delattr)  # type: ignore
    __dir__ = _ProxyLookup(dir, fallback=lambda self: [])  # type: ignore
    __class__ = _ProxyLookup(fallback=lambda self: type(self))  # type: ignore
    __instancecheck__ = _ProxyLookup(lambda self, other: isinstance(other, self))
    __subclasscheck__ = _ProxyLookup(lambda self, other: issubclass(other, self))
    # __class_getitem__ triggered through __getitem__
    __call__ = _ProxyLookup(lambda self, *args, **kwargs: self(*args, **kwargs))
    __len__ = _ProxyLookup(len)
    __length_hint__ = _ProxyLookup(operator.length_hint)
    __getitem__ = _ProxyLookup(operator.getitem)
    __setitem__ = _ProxyLookup(operator.setitem)
    __delitem__ = _ProxyLookup(operator.delitem)
    # __missing__ triggered through __getitem__
    __iter__ = _ProxyLookup(iter)
    __next__ = _ProxyLookup(next)

    # 删除部分,方便阅读
  1. self.__local什么时候被初始化?答:在__init__中,用到了叫名字改编(name mangling)这一特性…在python中以双下划线开头的变量被进行“名字改编”为_className__val, 例如__local,被改编为_ProxyLocal__local
  2. __init__(self, local, name),LocalProxy通常在两种常见下被实例化,一是通过Local(),二是通过LocalStack(), 前者直接通过Local()变量初始化,而后者通过一个函数(callable)初始化
  3. _get_current_object()获取LocalProxy所代理的那个对象
  4. 注意那些用ProxyLookup描述符实现的property,比较重要的如__setattr__, __getattr__, __delattr__, __iter__。在描述器中,都会先访问_get_current_object()获得所代理的具体对象,然后绑定函数,再执行具体的操作