基础篇

Jupyter Notebook

优点

  • 整合所有的资源
  • 交互性编程体验
  • 零成本重现结果

实践站点

列表与元组

列表和元组,都是 一个可以放置任意数据类型的有序集合

  1. l = [1, 2, 'hello', 'world'] # 列表中同时含有 int 和 string 类型的元素
  2. l
  3. [1, 2, 'hello', 'world']
  4. tup = ('jason', 22) # 元组中同时含有 int 和 string 类型的元素
  5. tup
  6. ('jason', 22)
  • 列表是动态的,长度大小不固定,可以随意地增加、删减或者改变元素 (mutable)
  • 元组是静态的,场地大小固定,无法增加删除或者改变 (immutable)
  • 都支持负数索引;
  • 都支持切片操作;
  • 都可以随意嵌套;
  • 两者可以通过 list()tuple() 函数相互转换;

列表和元组存储方式的差异

由于列表是动态的,所以它需要存储指针,来指向对应的元素。增加/删除的时间复杂度均为 O(1)。

  1. l = []
  2. l.__sizeof__() // 空列表的存储空间为 40 字节
  3. 40
  4. l.append(1)
  5. l.__sizeof__()
  6. 72 // 加入了元素 1 之后,列表为其分配了可以存储 4 个元素的空间 (72 - 40)/8 = 4
  7. l.append(2)
  8. l.__sizeof__()
  9. 72 // 由于之前分配了空间,所以加入元素 2,列表空间不变
  10. l.append(3)
  11. l.__sizeof__()
  12. 72 // 同上
  13. l.append(4)
  14. l.__sizeof__()
  15. 72 // 同上
  16. l.append(5)
  17. l.__sizeof__()
  18. 104 // 加入元素 5 之后,列表的空间不足,所以又额外分配了可以存储 4 个元素的空间

使用场景

  • 如果存储的数据和数量不变,那么肯定选用元组更合适
  • 如果存储的数据和数量是可变的,那么则用列表更合适

区别

  • 列表是动态的,长度可变,可以随意的增加、删除或者改变元素;列表的存储空间略大于元组,性能略逊于元组;
  • 元组是静态的,长度大小固定,不可对元素进行增加、删除、修改操作,元组相对于列表更加的轻量级、性能稍优;

思考题

  1. # 创建空列表
  2. # option A:list()是一个function call,Python的function call会创建stack,并且进行一系列参数检查的操作,比较expensive
  3. empty_list = list()
  4. # option B:[]是一个内置的C函数,可以直接被调用,因此效率高
  5. empty_list = []

字典与集合

字典是一系列无序元素的组合,其长度大小可变,元素可以任意的删除和改变,相比于列表和元组,字典的性能更优,特别是对于查找、添加和删除操作,字典都能在常数时间复杂度内完成。而集合和字典基本相同,唯一的区别,就是集合没有件和值的配对,是一系列无序的、唯一的元素组合。

  1. # 定义字典
  2. d = {'name': 'jason', 'age': 20}
  3. # 增加元素对'gender': 'male'
  4. d['gender'] = 'male'
  5. # 增加元素对'dob': '1999-02-01'
  6. d['dob'] = '1999-02-01'
  7. d
  8. {'name': 'jason', 'age': 20, 'gender': 'male', 'dob': '1999-02-01'}
  9. # 更新键'dob'对应的值
  10. d['dob'] = '1998-01-01'
  11. # 删除键为'dob'的元素对
  12. d.pop('dob')
  13. '1998-01-01'
  14. d
  15. {'name': 'jason', 'age': 20, 'gender': 'male'}
  16. # 定义集合
  17. s = {1, 2, 3}
  18. # 增加元素 4 到集合
  19. s.add(4)
  20. s
  21. {1, 2, 3, 4}
  22. # 从集合中删除元素 4
  23. s.remove(4)
  24. s
  25. {1, 2, 3}
  26. d = {'b': 1, 'a': 2, 'c': 10}
  27. # 根据字典键的升序排序
  28. d_sorted_by_key = sorted(d.items(), key=lambda x: x[0])
  29. # 根据字典值的升序排序
  30. d_sorted_by_value = sorted(d.items(), key=lambda x: x[1])

可以使用 get(key,default) 函数来进行字典索引。如果键不存在,调用该函数可以返回一个默认的值。

集合不支持索引操作,因为集合本质上是一个哈希表,和列表不一样。

字典和集合性能

字典和集合是进行性能高度优化的数据结构,特别是对于查找、添加和删除操作。

字典和集合的工作原理

字典和集合的内部结构都是一张哈希表

  • 对于字典而言,这张哈希表存储了哈希值,键和值这三个元素
  • 对于集合而言,区别就是哈希表内没有键和值的配对,只有单一的元素了

插入操作

每次向字典或集合插入一个元素时,Python 会首先计算键的哈希值(hash(key)),再和 mask = PyDicMinSize - 1 做与操作,计算这个元素应该插入哈希表的位置 index = hash(key) & mask。如果哈希表中此位置是空的,那么这个元素就会被插入其中。而如果此位置已被占用,Python 便会比较两个元素的哈希值和键是否相等。

  • 若两者都相等,则表明这个元素已经存在,如果值不同,则更新值。
  • 若两者中有一个不相等,这种情况我们通常称为哈希冲突(hash collision),意思是两个元素的键不相等,但是哈希值相等。这种情况下,Python 便会继续寻找表中空余的位置,直到找到位置为止。

查找操作

先通过哈希值找到目标位置,然后比较哈希表这个位置中元素的哈希值和键,与需要查找的元素是否相等,如果相等,则直接返回,否则继续查找,知道为空或抛出异常为止

删除操作

暂时对这个位置得到元素赋予一个特殊的值,等到重新调整哈希表的大小时,再将其删除。

字符串

  • Python 中字符串使用单引号、双引号或三引号表示,三者意义相同,并没有什么区别。其中,三引号的字符串通常用在多行字符串的场景。
  • Python 中字符串是不可变的(前面所讲的新版本 Python 中拼接操作’+='是个例外)。因此,随意改变字符串中字符的值,是不被允许的。
  • Python 新版本(2.5+)中,字符串的拼接变得比以前高效了许多,你可以放心使用。
  • Python 中字符串的格式化(string.format,f)常常用在输出、日志的记录等场景。

输入与输出

输入输出基础

生产环境中使用强制转换时,请记得加上 try except

文件输入输出

所有 I/O 都应该进行错误处理。因为 I/O 操作可能会有各种各样的情况出现,而一个健壮(robust)的程序,需要能应对各种情况的发生,而不应该崩溃(故意设计的情况除外)。

JSON 序列化与实战

  • json.dumps() 函数,接受 python 的基本数据类型,然后将其序列化为 string;
  • json.loads() 函数,接受一个合法字符串,然后将其序列化为 python 的基本数据类型;

条件与循环

  • 在条件语句中,if 可以单独使用,但是 elif 和 else 必须和 if 同时搭配使用;而 If 条件语句的判断,除了 boolean 类型外,其他的最好显示出来。
  • 在 for 循环中,如果需要同时访问索引和元素,你可以使用 enumerate() 函数来简化代码。
  • 写条件与循环时,合理利用+continue+或者+break+来避免复杂的嵌套,是十分重要的。
  • 要注意条件与循环的复用,简单功能往往可以用一行直接完成,极大地提高代码质量与效率。

异常处理

  • 异常,通常是指程序运行的过程中遇到了错误,终止并退出。我们通常使用 try except 语句去处理异常,这样程序就不会被终止,仍能继续执行。
  • 处理异常时,如果有必须执行的语句,比如文件打开后必须关闭等等,则可以放在 finally block 中。
  • 异常处理,通常用在你不确定某段代码能否成功执行,也无法轻易判断的情况下,比如数据库的连接、读取等等。正常的 flow-control 逻辑,不要使用异常处理,直接用条件语句解决就可以了。

自定义函数

  • Python 中函数的参数可以接受任意的数据类型,使用起来需要注意,必要时请在函数开头加入数据类型的检查;
  • 和其他语言不同,Python 中函数的参数可以设定默认值;
  • 嵌套函数的使用,能保证数据的隐私性,提高程序运行效率;
  • 合理地使用闭包,则可以简化程序的复杂度,提高可读性;

匿名函数

优点:

  • 减少代码的重复性;
  • 模块化代码;

map(function,iterable)

表示对 iterable 中的每个元素,都运用 function 这个函数,最后返回一个新的可遍历的集合。

  1. def square(x):
  2. return x**2
  3. squared = map(square, [1, 2, 3, 4, 5]) # [2, 4, 6, 8, 10]

filter(function,iterable)

表示对 iterable 中的每个元素,都使用 function 判断,并返回 True 或者 False,最后将返回 True 的元素组成一个新的可遍历的集合。

  1. l = [1, 2, 3, 4, 5]
  2. new_list = filter(lambda x: x % 2 == 0, l) # [2, 4]

reduce(function,iterable)

规定它有两个参数,表示对 iterable 中的每个元素以及上一次调用后的结果,运用 function 进行计算,所以最后返回的是一个单独的数值。

  1. l = [1, 2, 3, 4, 5]
  2. product = reduce(lambda x, y: x * y, l) # 1*2*3*4*5 = 120

面向对象

基本概念

  • 类:一群有着相似性的事物的集合;
  • 对象:集合中的一个事物;
  • 属性:对象的某个静态特征;
  • 函数:对象某个动态能力

三要素:

  • 继承
  • 封装
  • 多态

模块化编程

  • 通过绝对路径和相对路径,我们可以 import 模块;
  • 在大型工程中模块化非常重要,模块的索引要通过绝对路径来做,而绝对路径从程序的根目录开始;
  • 记着巧用 if __name__ == "__main__" 来避开 import 时执行;

进阶篇

Python 对象的比较、拷贝

  • 比较操作符 == 表示比较对象间的值是否相等,而 is 表示比较对象的标识是否相等,即它们是否指向同一个内存地址。
  • 比较操作符 is 效率优于 ==,因为 is 操作符无法被重载,执行 is 操作只是简单的获取对象的 ID,并进行比较;而 == 操作符则会递归地遍历对象的所有值,并逐一比较。
  • 浅拷贝中的元素,是原对象中子对象的引用,因此,如果原对象中的元素是可变的,改变其也会影响拷贝后的对象,存在一定的副作用。
  • 深度拷贝则会递归地拷贝原对象中的每一个子对象,因此拷贝后的对象和原对象互不相关。另外,深度拷贝中会维护一个字典,记录已经拷贝的对象及其 ID,来提高效率并防止无限递归的发生。

值传递与引用传递

常见的参数传递有 2 种:

  • 值传递:通常就是拷贝对象的值,然后传递给函数里的新变量,原变量和新变量之间相互独立,互不影响
  • 引用传递:通常是指把参数的引用传给新的变量,这样,原变量和新变量就会指向同一块内存地址。

准确来说, python 的参数传递是 赋值传递 ,或者叫做对象的 引用传递 ,python 里所有的数据类型都是对象,所以参数传递时,只是让新变量与原变量指向相同的对象而已,并不存在值传递或引用传递一说。

需要注意的是,这里的赋值或对象的引用传递,不是指一个具体的内存地址,二十指一个具体的对象。

  • 如果对象是可变的,当其改变时,所有指向这个对象的变量都会改变;
  • 如果对象不可变,简单的赋值只能改变其中一个变量的值,其余变量则不受影响;

装饰器

函数也是对象

  1. def func(message):
  2. print('Got a message: {}'.format(message))
  3. send_message = func
  4. send_message('hello world')
  5. # 输出
  6. Got a message: hello world

函数可以作为函数参数

  1. def get_message(message):
  2. return 'Got a message: ' + message
  3. def root_call(func, message):
  4. print(func(message))
  5. root_call(get_message, 'hello world')
  6. # 输出
  7. Got a message: hello world

函数可以嵌套函数

  1. def func(message):
  2. def get_message(message):
  3. print('Got a message: {}'.format(message))
  4. return get_message(message)
  5. func('hello world')
  6. # 输出
  7. Got a message: hello world

函数的返回值也可以是函数对象(闭包)

  1. def func_closure():
  2. def get_message(message):
  3. print('Got a message: {}'.format(message))
  4. return get_message
  5. send_message = func_closure()
  6. send_message('hello world')
  7. # 输出
  8. Got a message: hello world

简单使用装饰器

  1. def my_decorator(func):
  2. def wrapper():
  3. print('wrapper of decorator')
  4. func()
  5. return wrapper
  6. def greet():
  7. print('hello world')
  8. greet = my_decorator(greet)
  9. greet()
  10. # 输出
  11. wrapper of decorator
  12. hello world

更优雅的写法

  1. def my_decorator(func):
  2. def wrapper():
  3. print('wrapper of decorator')
  4. func()
  5. return wrapper
  6. @my_decorator
  7. def greet():
  8. print('hello world')
  9. greet()

带参数的装饰器

  1. def my_decorator(func):
  2. def wrapper(message):
  3. print('wrapper of decorator')
  4. func(message)
  5. return wrapper
  6. @my_decorator
  7. def greet(message):
  8. print(message)
  9. greet('hello world')
  10. # 输出
  11. wrapper of decorator
  12. hello world

带自定义参数的装饰器

  1. def repeat(num):
  2. def my_decorator(func):
  3. def wrapper(*args, **kwargs):
  4. for i in range(num):
  5. print('wrapper of decorator')
  6. func(*args, **kwargs)
  7. return wrapper
  8. return my_decorator
  9. @repeat(4)
  10. def greet(message):
  11. print(message)
  12. greet('hello world')
  13. # 输出:
  14. wrapper of decorator
  15. hello world
  16. wrapper of decorator
  17. hello world
  18. wrapper of decorator
  19. hello world
  20. wrapper of decorator
  21. hello world

上述 green() 函数被装饰以后,它的元信息会发生改变,可勇敢 greet__name__ 来查看。可通过内置装饰器来解决这个问题

  1. import functools
  2. def my_decorator(func):
  3. @functools.wraps(func)
  4. def wrapper(*args, **kwargs):
  5. print('wrapper of decorator')
  6. func(*args, **kwargs)
  7. return wrapper
  8. @my_decorator
  9. def greet(message):
  10. print(message)
  11. greet.__name__
  12. # 输出
  13. 'greet'

类装饰器

  1. class Count:
  2. def __init__(self, func):
  3. self.func = func
  4. self.num_calls = 0
  5. def __call__(self, *args, **kwargs):
  6. self.num_calls += 1
  7. print('num of calls is: {}'.format(self.num_calls))
  8. return self.func(*args, **kwargs)
  9. @Count
  10. def example():
  11. print("hello world")
  12. example()
  13. # 输出
  14. num of calls is: 1
  15. hello world
  16. example()
  17. # 输出
  18. num of calls is: 2
  19. hello world

装饰器支持嵌套使用

  1. @decorator1
  2. @decorator2
  3. @decorator3
  4. def func():
  5. ...
  6. # 等价于
  7. decorator1(decorator2(decorator3(func)))

装饰器使用场景:

  • 身份认证
  • 日志记录
  • 输入合理性检查
  • 缓存(LRU cache)

metaclass

metaclass 是 Python 黑魔法级别的语言特性,它可以改变正常 Python 类型的创建过程。

  • 所有 Python 的用户定义类,都是 type 这个类的实例
  • 用户自定义类,只不过是 type 类的 __ call __ 运算符重载
  • metaclass 是 type 的子类,通过替换 type 的 __ call __ 运算符重载机制,超越变形正常的类
  1. class Mymeta(type):
  2. def __init__(self, name, bases, dic):
  3. super().__init__(name, bases, dic)
  4. print('===>Mymeta.__init__')
  5. print(self.__name__)
  6. print(dic)
  7. print(self.yaml_tag)
  8. def __new__(cls, *args, **kwargs):
  9. print('===>Mymeta.__new__')
  10. print(cls.__name__)
  11. return type.__new__(cls, *args, **kwargs)
  12. def __call__(cls, *args, **kwargs):
  13. print('===>Mymeta.__call__')
  14. obj = cls.__new__(cls)
  15. cls.__init__(cls, *args, **kwargs)
  16. return obj
  17. class Foo(metaclass=Mymeta):
  18. yaml_tag = '!Foo'
  19. def __init__(self, name):
  20. print('Foo.__init__')
  21. self.name = name
  22. def __new__(cls, *args, **kwargs):
  23. print('Foo.__new__')
  24. return object.__new__(cls)
  25. foo = Foo('foo')

迭代器和生成器

  • 容器时可迭代对象,可迭代对象调用 iter() 函数,可以得到一个迭代器。迭代器可以通过 next() 函数来得到下一个元素,从而支持遍历
  • 生成器时一种特殊的迭代器,合理使用生成器,可以降低内存占用、优化程序结构、提高程序速度
  • 生成器在 Python 2 的版本上,是协程的一种重要实现方式;而 Python 3.5 引入的 async、await 语法糖,生成器实现协程的方式就已经落后了。

协程

协程是实现并发编程的一种方式

  • 协程和多线程的区别,主要在于两点,一是协程为单线程;二是协程由用户决定,在哪些地方交出控制权,切换到下一个任务
  • 协程的写法更加简洁清晰;把 async/await 语法和 create_task 结合起来用,对于中小级别的并发需求已经毫无压力

生产者/消费者 模型

  1. import asyncio
  2. import random
  3. async def consumer(queue, id):
  4. while True:
  5. val = await queue.get()
  6. print('{} get a val: {}'.format(id, val))
  7. await asyncio.sleep(1)
  8. async def producer(queue, id):
  9. for i in range(5):
  10. val = random.randint(1, 10)
  11. await queue.put(val)
  12. print('{} put a val: {}'.format(id, val))
  13. await asyncio.sleep(1)
  14. async def main():
  15. queue = asyncio.Queue()
  16. consumer_1 = asyncio.create_task(consumer(queue, 'consumer_1'))
  17. consumer_2 = asyncio.create_task(consumer(queue, 'consumer_2'))
  18. producer_1 = asyncio.create_task(producer(queue, 'producer_1'))
  19. producer_2 = asyncio.create_task(producer(queue, 'producer_2'))
  20. await asyncio.sleep(10)
  21. consumer_1.cancel()
  22. consumer_2.cancel()
  23. await asyncio.gather(consumer_1, consumer_2, producer_1, producer_2, return_exceptions=True)
  24. %time asyncio.run(main())
  25. ########## 输出 ##########
  26. producer_1 put a val: 5
  27. producer_2 put a val: 3
  28. consumer_1 get a val: 5
  29. consumer_2 get a val: 3
  30. producer_1 put a val: 1
  31. producer_2 put a val: 3
  32. consumer_2 get a val: 1
  33. consumer_1 get a val: 3
  34. producer_1 put a val: 6
  35. producer_2 put a val: 10
  36. consumer_1 get a val: 6
  37. consumer_2 get a val: 10
  38. producer_1 put a val: 4
  39. producer_2 put a val: 5
  40. consumer_2 get a val: 4
  41. consumer_1 get a val: 5
  42. producer_1 put a val: 2
  43. producer_2 put a val: 8
  44. consumer_1 get a val: 2
  45. consumer_2 get a val: 8
  46. Wall time: 10 s

并发编程之 Futures

区别并发和并行

  • 并发通常应用与 I/O 操作频繁的场景,比如你要从网站上下载多个文件, I/O 操作的时间可能比 CPU 运行处理的时间长得多,通过线程和任务之间互相切换的方式实现,但同一时刻,只允许有一个线程或任务执行
  • 并行更多应用于 CPU heavy 的场景,比如 MapReduce 中的并行计算,为了加快运算速度,一般会用多台机器,多个处理器来完成。可以让多个进程完全同步同时的执行

Python 中之所以同一时刻只运行一个线程运行,其实是由于全局解释锁的存在。但对 I/O 操作而言,当其被 block 的时候,全局解释器锁便会被释放,使气体线程继续执行。

  1. import concurrent.futures
  2. import requests
  3. import threading
  4. import time
  5. def download_one(url):
  6. resp = requests.get(url)
  7. print('Read {} from {}'.format(len(resp.content), url))
  8. # 版本 1
  9. def download_all(sites):
  10. with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
  11. executor.map(download_one, sites)
  12. # 版本 2
  13. def download_all(sites):
  14. with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
  15. to_do = []
  16. for site in sites:
  17. future = executor.submit(download_one, site)
  18. to_do.append(future)
  19. for future in concurrent.futures.as_completed(to_do):
  20. future.result()
  21. def main():
  22. sites = [
  23. 'https://en.wikipedia.org/wiki/Portal:Arts',
  24. 'https://en.wikipedia.org/wiki/Portal:History',
  25. 'https://en.wikipedia.org/wiki/Portal:Society',
  26. 'https://en.wikipedia.org/wiki/Portal:Biography',
  27. 'https://en.wikipedia.org/wiki/Portal:Mathematics',
  28. 'https://en.wikipedia.org/wiki/Portal:Technology',
  29. 'https://en.wikipedia.org/wiki/Portal:Geography',
  30. 'https://en.wikipedia.org/wiki/Portal:Science',
  31. 'https://en.wikipedia.org/wiki/Computer_science',
  32. 'https://en.wikipedia.org/wiki/Python_(programming_language)',
  33. 'https://en.wikipedia.org/wiki/Java_(programming_language)',
  34. 'https://en.wikipedia.org/wiki/PHP',
  35. 'https://en.wikipedia.org/wiki/Node.js',
  36. 'https://en.wikipedia.org/wiki/The_C_Programming_Language',
  37. 'https://en.wikipedia.org/wiki/Go_(programming_language)'
  38. ]
  39. start_time = time.perf_counter()
  40. download_all(sites)
  41. end_time = time.perf_counter()
  42. print('Download {} sites in {} seconds'.format(len(sites), end_time - start_time))
  43. if __name__ == '__main__':
  44. main()
  45. ## 输出
  46. Read 151021 from https://en.wikipedia.org/wiki/Portal:Mathematics
  47. Read 129886 from https://en.wikipedia.org/wiki/Portal:Arts
  48. Read 107637 from https://en.wikipedia.org/wiki/Portal:Biography
  49. Read 224118 from https://en.wikipedia.org/wiki/Portal:Society
  50. Read 184343 from https://en.wikipedia.org/wiki/Portal:History
  51. Read 167923 from https://en.wikipedia.org/wiki/Portal:Geography
  52. Read 157811 from https://en.wikipedia.org/wiki/Portal:Technology
  53. Read 91533 from https://en.wikipedia.org/wiki/Portal:Science
  54. Read 321352 from https://en.wikipedia.org/wiki/Computer_science
  55. Read 391905 from https://en.wikipedia.org/wiki/Python_(programming_language)
  56. Read 180298 from https://en.wikipedia.org/wiki/Node.js
  57. Read 56765 from https://en.wikipedia.org/wiki/The_C_Programming_Language
  58. Read 468461 from https://en.wikipedia.org/wiki/PHP
  59. Read 321417 from https://en.wikipedia.org/wiki/Java_(programming_language)
  60. Read 324039 from https://en.wikipedia.org/wiki/Go_(programming_language)
  61. Download 15 sites in 0.19936635800002023 seconds

并发编程之 Asyncio

  1. import asyncio
  2. import aiohttp
  3. import time
  4. async def download_one(url):
  5. async with aiohttp.ClientSession() as session:
  6. async with session.get(url) as resp:
  7. print('Read {} from {}'.format(resp.content_length, url))
  8. async def download_all(sites):
  9. tasks = [asyncio.create_task(download_one(site)) for site in sites]
  10. await asyncio.gather(*tasks)
  11. def main():
  12. sites = [
  13. 'https://en.wikipedia.org/wiki/Portal:Arts',
  14. 'https://en.wikipedia.org/wiki/Portal:History',
  15. 'https://en.wikipedia.org/wiki/Portal:Society',
  16. 'https://en.wikipedia.org/wiki/Portal:Biography',
  17. 'https://en.wikipedia.org/wiki/Portal:Mathematics',
  18. 'https://en.wikipedia.org/wiki/Portal:Technology',
  19. 'https://en.wikipedia.org/wiki/Portal:Geography',
  20. 'https://en.wikipedia.org/wiki/Portal:Science',
  21. 'https://en.wikipedia.org/wiki/Computer_science',
  22. 'https://en.wikipedia.org/wiki/Python_(programming_language)',
  23. 'https://en.wikipedia.org/wiki/Java_(programming_language)',
  24. 'https://en.wikipedia.org/wiki/PHP',
  25. 'https://en.wikipedia.org/wiki/Node.js',
  26. 'https://en.wikipedia.org/wiki/The_C_Programming_Language',
  27. 'https://en.wikipedia.org/wiki/Go_(programming_language)'
  28. ]
  29. start_time = time.perf_counter()
  30. asyncio.run(download_all(sites))
  31. end_time = time.perf_counter()
  32. print('Download {} sites in {} seconds'.format(len(sites), end_time - start_time))
  33. if __name__ == '__main__':
  34. main()
  35. ## 输出
  36. Read 63153 from https://en.wikipedia.org/wiki/Java_(programming_language)
  37. Read 31461 from https://en.wikipedia.org/wiki/Portal:Society
  38. Read 23965 from https://en.wikipedia.org/wiki/Portal:Biography
  39. Read 36312 from https://en.wikipedia.org/wiki/Portal:History
  40. Read 25203 from https://en.wikipedia.org/wiki/Portal:Arts
  41. Read 15160 from https://en.wikipedia.org/wiki/The_C_Programming_Language
  42. Read 28749 from https://en.wikipedia.org/wiki/Portal:Mathematics
  43. Read 29587 from https://en.wikipedia.org/wiki/Portal:Technology
  44. Read 79318 from https://en.wikipedia.org/wiki/PHP
  45. Read 30298 from https://en.wikipedia.org/wiki/Portal:Geography
  46. Read 73914 from https://en.wikipedia.org/wiki/Python_(programming_language)
  47. Read 62218 from https://en.wikipedia.org/wiki/Go_(programming_language)
  48. Read 22318 from https://en.wikipedia.org/wiki/Portal:Science
  49. Read 36800 from https://en.wikipedia.org/wiki/Node.js
  50. Read 67028 from https://en.wikipedia.org/wiki/Computer_science
  51. Download 15 sites in 0.062144195078872144 seconds

Asyncio 是单线程的,但其内部 event loop 的机制,可以让它并发地运行多个不同的任务,并且比多线程享有更大的自主控制权。

Asyncio 中的任务, 在运行过程中不会被打断,因此不会出现 race condition 的情况。尤其是在 I/O 操作 heavy 的场景下, Asyncio 比多线程的运行效率更高,因此 Asyncio 内部任务切换的损耗,远比线程切换的损耗要小,并且 Asyncio 可以开启的任务数量,也比多线程中的线程数列多得多。

但需要注意的是,很多情况下,使用 Asyncio 需要特定第三方库的支持,而如果 I/O 操作很快,并不 heavy ,建议使用多线程来解决问题。

GIL(全局解释器锁)

CPython 引进 GIL 主要是由于:

  • 设计者为了规避类似于内存管理这样的复杂的竞争风险问题
  • 因为 CPython 大量使用 C 语言库,但大部分 C 语言库都不是原生线程安全的(线程安全会降低性能和增加复杂度)

GIL 的设计,主要是为了方便 CPython 解释器层面的编写者,而不是 Python 应用层面的程序员。

可以使用 import dis 的方式将代码编译成 bytecode

垃圾回收机制

  • 垃圾回收是 Python 自带的机制,用于自动释放不会再用到的内存空间
  • 引用计数是其最简单的实现,不过切记,这只是充分非必要条件,因为循环引用需要通过不可达判定,来确定是否可以回收
  • Python 的自动回收算法包括标记清除和分代收集,主要针对的是循环引用的垃圾收集
  • 调试内存泄漏方便,objgraph 是很好的可视化分析工具

编程规范

阅读者的体验 > 编程者的体验 > 机器的体验

  • 学会合理分解代码,提高代码可读性
  • 合理利用 assert(线上环境禁止使用)
  • 启用上下文管理器和 with 语句精简代码
  • 单元测试
  • pdf & cprofile

Python核心技术与实战 笔记的更多相关文章

  1. 关于Python网络爬虫实战笔记③

    Python网络爬虫实战笔记③如何下载韩寒博客文章 Python网络爬虫实战笔记③如何下载韩寒博客文章 target:下载全部的文章 1. 博客列表页面规则 也就是, http://blog.sina ...

  2. 关于Python网络爬虫实战笔记①

    python网络爬虫项目实战笔记①如何下载韩寒的博客文章 python网络爬虫项目实战笔记①如何下载韩寒的博客文章 1. 打开韩寒博客列表页面 http://blog.sina.com.cn/s/ar ...

  3. Python核心技术与实战——十九|一起看看Python全局解释器锁GIL

    我们在前面的几节课里讲了Python的并发编程的特性,也了解了多线程编程.事实上,Python的多线程有一个非常重要的话题——GIL(Global Interpreter Lock).我们今天就来讲一 ...

  4. Python核心技术与实战——六|异常处理

    和其他语言一样,Python中的异常处理是很重要的机制和代码规范. 一.错误与异常 通常来说程序中的错误分为两种,一种是语法错误,另一种是异常.首先要了解错误和异常的区别和联系. 语法错误比较容易理解 ...

  5. Python核心技术与实战——十六|Python协程

    我们在上一章将生成器的时候最后写了,在Python2中生成器还扮演了一个重要的角色——实现Python的协程.那什么是协程呢? 协程 协程是实现并发编程的一种方式.提到并发,肯很多人都会想到多线程/多 ...

  6. Python核心技术与实战——二一|巧用上下文管理器和with语句精简代码

    我们在Python中对于with的语句应该是不陌生的,特别是在文件的输入输出操作中,那在具体的使用过程中,是有什么引伸的含义呢?与之密切相关的上下文管理器(context manager)又是什么呢? ...

  7. Python核心技术与实战——二十|assert的合理利用

    我们平时在看代码的时候,或多或少会看到过assert的存在,并且在有些code review也可以通过增加assert来使代码更加健壮.但是即便如此,assert还是很容易被人忽略,可是这个很不起眼的 ...

  8. Python核心技术与实战——二十|Python的垃圾回收机制

    今天要讲的是Python的垃圾回收机制 众所周知,我们现在的计算机都是图灵架构.图灵架构的本质,就是一条无限长的纸带,对应着我们的存储器.随着寄存器.异失性存储器(内存)和永久性存储器(硬盘)的出现, ...

  9. Python核心技术与实战——十八|Python并发编程之Asyncio

    我们在上一章学习了Python并发编程的一种实现方法——多线程.今天,我们趁热打铁,看看Python并发编程的另一种实现方式——Asyncio.和前面协程的那章不太一样,这节课我们更加注重原理的理解. ...

随机推荐

  1. Python练习100题

    Python练习100题 题目:有1.2.3.4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少? #Filename:001.py cnt = 0#count the sum of res ...

  2. Hadoop运行模式

    Hadoop运行模式 (1)本地模式(默认模式): 不需要启用单独进程,直接可以运行,测试和开发时使用. 即在一台机器上进行操作,仅为单机版. 本地运行Hadoop官方MapReduce案例 操作命令 ...

  3. python3基础之 字符串切片

    一.python3中,可迭代对象有:列表.元组.字典.字符串:常结合for循环使用:均可使用索引切片 实例: str = ' #str[start:stop:step] 遵循[左闭右开]规则 prin ...

  4. linux常见配置文件路径

    1:/etc/sysconfig/i18n                        (语言配置文件). 2:/etc/sysconfig/network-scripts/ifcfg-eth0   ...

  5. 【浅析】|白话布隆过滤器BloomFilter

    通过本文将了解到以下内容: 查找问题的一般思路 布隆过滤器的基本原理 布隆过滤器的典型应用 布隆过滤器的工程实现 场景说明: 本文阐述的场景均为普通单机服务器.并非分布式大数据平台,因为在大数据平台下 ...

  6. python logging模块小记

    1.简单的将日志打印到屏幕 import logging logging.debug('This is debug message') logging.info('This is info messa ...

  7. 一文了解Nuget的使用

    Nuget介绍 官网定义:NuGet是.NET的软件包管理器(免费).NuGet客户端工具提供了生成和使用软件包的能力.NuGet Gallery 是所有软件包作者和消费者都使用的中央软件包存储库. ...

  8. C#开发安卓自学笔记1

    今天开始研究了下C#开发安卓,刚开始什么都不懂,学过安卓的同学们也是用Java开发的,虽然两者开发差别不大,但是还是有差别的 // Set our view from the "main&q ...

  9. SmartSVN提示 svn: File has inconsistent newlines 解决

    用SmartSVN提交代码的时候提示:svn: File has inconsistent newlines 本文转自:http://www.youduoshao.com/2014-10-05/201 ...

  10. 过滤器和监听器实现用户的在线登录人数,以及设置session时长。

    过滤器: package com.bjsxt.filter; import java.io.IOException; import javax.servlet.Filter; import javax ...