为什么使用object.__setattr__

werkzeug 0.6.1中Local的初始化是这样的: class Local(object): __slots__ = ('__storage__', '__lock__') def __init__(self): object.__setattr__(self, '__storage__', {}) object.__setattr__(self, '__lock__', allocate_lock()) 我当时很奇怪为什么要用object.__setattr__, 而不是直接用self.__storage__, 当我直接用self.__storage__ = {}实现的时候才发现问题: self.__storage__会调用__setattr__,而__setattr__中会调用self.__lock__.acquire(),因为此时self.__lock__还没有定义, 所以会调用self.__getattr__,而self.__getattr__中也会调用self.__lock__.acquire(), 此后就会一直调用self.__getattr__,最终导致StackOverflow。 而显示调用object.__setattr__就不会触发Local内部的__setattr__,从而避免上述情况。而且两者的效果是一样的,object.__setattr__的第一个参数是self,也就是这个实例,所以并不用担心是不是在父类定义了一个公共属性。 类似的还有使用object.__getattribute__的情况,一般也是为了避免无限递归。

July 23, 2019 · 1 min · Egbert Ke

理解werkzeug中的Local对象

ThreadLocal是线程级别的local,如果在greenlet或者协程这种微线程环境下,或者在多个请求共用一个线程的情况下,线程级别是不够的。ThreadLocal是thread-safe和thread-specific的, 而有些情况需要greenlet-safe和greenlet-specific或者request-safe和request-specific。 werkzeug 0.1版中Local的实现是这样的: try: from py.magic import greenlet get_current_greenlet = greenlet.getcurrent del greenlet except (RuntimeError, ImportError): get_current_greenlet = lambda: None try: from thread import get_ident as get_current_thread from threading import Lock except ImportError: from dummy_thread import get_ident as get_current_thread from dummy_threading import Lock from werkzeug.utils import ClosingIterator def get_ident(): """ Return a unique number for the current greenlet in the current thread. """ return hash((get_current_thread(), get_current_greenlet())) class Local(object): def __init__(self): self....

July 18, 2019 · 2 min · Egbert Ke

ThreadLocal

TheadLocal 用于多线程环境下,线程之间可以使用相同的变量,而这个变量只与当前线程环境有关。werkzeug中有类似的实现,使每个路由处理函数都可使用相同的request变量,而这个对象的内容只与当前请求有关。 例如:1 import threading # 创建全局ThreadLocal对象: local_school = threading.local() def process_student(): # 获取当前线程关联的student: std = local_school.student print('Hello, %s (in %s)' % (std, threading.current_thread().name)) def process_thread(name): # 绑定ThreadLocal的student: local_school.student = name process_student() t1 = threading.Thread(target= process_thread, args=('Alice',), name='Thread-A') t2 = threading.Thread(target= process_thread, args=('Bob',), name='Thread-B') t1.start() t2.start() t1.join() t2.join() 执行结果: Hello, Alice (in Thread-A) Hello, Bob (in Thread-B) 廖雪峰的官方网站 ↩︎

July 17, 2019 · 1 min · Egbert Ke

git 切换 tag

有一种源码学习的方法是这样的:从最初的版本开始看,有的很大的开源项目最初可能就只有几百行。我们可以先把项目clone到本地,然后切换到最初般。 列出所有版本: git tag 若一个tag都没有,则可能是因为你先fork了这个项目,然后本地再pull下来的,这种情况得先执行: git fetch 然后,切换到指定版本: git checkout tagname 切回到主分支: git checkout master

July 12, 2019 · 1 min · Egbert Ke

Python中的async/await

async/await是实现异步IO的语法糖,是Python3.7新出的关键字。async def可创建协程,而await可用来等待一个可等待对象的执行完成。这大大简化了协程的创建(在Python2中创建协程需要yield和send协同操作) 下面这个例子很简洁的说明了什么是异步IO1: import asyncio async def count(): print("One") await asyncio.sleep(1) print("Two") async def main(): await asyncio.gather(count(), count(), count()) if __name__ == "__main__": import time s = time.perf_counter() asyncio.run(main()) elapsed = time.perf_counter() - s print(f"{__file__} executed in {elapsed:0.2f} seconds.") 运行结果: $ python3 countasync.py One One One Two Two Two countasync.py executed in 1.01 seconds. gather会等待所有协程都返回后再返回一个结果列表,as_completed会当协程返回后立即返回: >>> import asyncio >>> async def coro(seq) -> list: ... """'IO' wait time is proportional to the max element....

July 9, 2019 · 1 min · Egbert Ke