《流畅的Python》——Python中一些有意思的东西整理

4 其他流程控制工具

  • else在for循环中的应用
  • 函数按值调用
  • 位置参数、可变参数、关键字参数
  • 将*、**理解为从列表/元组、字典中解包参数
  • lamda表达式
  • doc
  • annotations

数据结构

  • append
  • extend
  • insert
  • remove
  • pop(i)
  • clear
  • index(x[, start[, end]])
  • count(x)
  • reverse
  • sort
  • copy
  • collections.deque
    • popleft
  • 列表推导式
  • 嵌套的列表推导式
  • zip函数
  • del关键字
  • tuple声明方式
  • 打包/解包

Set

  • {xxx}创建set,但a={}是一个空字典
    • | & ^

dict

  • list(dict) return list of key
  • sorted(dict)按插入顺序

loop

  • items
  • enumerate
  • 如果要按某个指定顺序循环一个序列,可以用 sorted() 函数,它可以在不改动原序列的基础上返回一个新的排好序的序列
  • is/not is

module

  • name 属性
  • 模块可以包含可执行的语句以及函数定义。这些语句用于初始化模块。它们仅在模块 第一次 在 import 语句中被导入时才执行。
  • 每个模块都有它自己的私有符号表,该表用作模块中定义的所有函数的全局符号表。因此,模块的作者可以在模块内使用全局变量,而不必担心与用户的全局变量发生意外冲突。
  • 变量表概念、名称
  • module.reload()
if __name__ == "__main__":
	stat
	stat
  • sys变量
  • dir函数
  • __init.py__文件的作用
  • 相对导入、绝对导入

input & output

  • 字符串前加f的作用
  • .format()
  • str
  • repr
  • f-
f'{:int}'
f'{:!a}' -> ascii()
f'{:!s}' -> str()
f'{:!r}' -> repr()
  • str.rjust()、ljust()、center()、zfill()
  • %操作符进行格式化

file manipulate

  • open
    • mode:r w a r+ b
  • f.readline(size)
  • list(f)
  • f.readlines()
  • f.write()
  • f.seek(offset, from_what) from_what值为0或1
  • f.tell()

json

  • json.dumps
  • json.dump
  • json.load

ch-1 Python数据模型

  • namedtuple用于构建只有少量属性但是没有方法的对象
import collections
Card = collections.namedtuple('Card', ['rank', 'suit'])

  • 特殊方法的存在是为了被Python解释器调用的, 没有object.len(),应该用len(object)
  • 如果是python内置类型,比如list,str,bytearray,CPython会直接返回 PyVarObject 里的 ob_size 属性
  • PyVarObject 时表示内存中长度可变的内置对象的C语言结构体
  • __repr__把一个对象用字符串表达出来以便辨认
  • __str__会被str()调用,或者用print函数打印一个对象时被调用,如果对象没有实现__str__方法,解释器则会去调用__repr__方法
  • 默认情况,自定义类的实例总被认为真的,除非该类实现了__bool__或__len__,如果没有__bool__,bool()则调用__len__,0为False,否则为True
  • 如果x是一个内置类型的实例,那么CPython在len的实现中则会直接从C结构体里读取对象的长度,这样则使得运行高效

ch-2 序列构成的数组

  • 容器序列存放任意类型的对象的引用,例如list、tuple、collections.deque
  • 扁平序列存放的是值,其实质为一段连续的内存空间,只能存放字符、字节和数值这种基础类型

列表推导

  • python2中列表推导的for关键词之后的赋值操作可能会影响上下文中的同名变量,而python3中列表推导有了局部作用域,表达式内部的变量和赋值只在局部起作用

tuple

  • tuple不仅是不可变列表,将每个元素的字段位置信息利用起来可以更加高效,比如namedtuple就是
  • 元组拆包
    • 平行赋值 a, b = my_tuple
    • 交换两值 a, b = b, a
    • 用*运算符将某可迭代对象拆开作为函数参数
  • 用namedtuple构建的类的实例所消耗的内存跟元组是一样的,但是python不会用__dict__属性来存储实例的属性,所以namedtuple构建的类要比普通类节省空间
  • 两个参数,第一个是类名,第二个是类中各个字段的名字,后者可以由数个字符串组成的可迭代对象,或者由空格分开的字段名
  • 可以用字段名或位置信息获取字段信息
  • namedtuple还有诸如_fields类属性、_make(iterable)类方法、_asdict()实例方法等

切片

  • 如果赋值的对象是切片,那么语句的有操作数必须是一个可迭代对象,即便是单值也要转换,否则会引起TypeError

序列操作

  • +、*都不会改变原有序列,而是新建一个包含同样类型数据的序列作为result
  • 如果a实现了__iadd__,那么a += b则会调用它,如果没有,则调用a = a + b即__add__
  • +=、*=对于可变序列则会就地修改、不可变序列则会创建一个新的序列并赋值
  • 下面的语句会出错、但是t[2]仍然被成功修改
t = (1, 2, [30, 40])
t[2] += [50, 60]
print(t)

  • 因为解释器先增量、再赋值
  • 所以
    • 不要把可变对象放在元组里面
    • 增量赋值不是一个原子操作
  • list.sort()就地修改
  • sorted(seq, key=, reverse=)新建副本
  • position = bisect.bisect(seq, item, lo=, hi=)
  • bisect.insort(seq, item, lo=, hi=)
  • array.array.tofile(fp)
  • array.array.fromfile(fp, size)
  • collections.deque方法如下
appendleft()
append()
extendleft(iter)
# extendleft将iter中从左到右依次添加到deque最左侧,所以iter中元素会逆序出现在deque中
extend(iter)
rotate(n) # 右旋n位
pop()
popleft()

ch-3 字典和集合

  • 可散列:
    • 整个生命周期中,散列值不变
    • 必须实现__hash__,__eq__方法
    • 若两个可散列对象相等,那么他们的散列值一定是一样的
    • frozenset只可容纳可散列类型
    • 自定义的对象一般都是可散列的,散列值就是他们的id()值

字典

  • 字典推导
    new_dict = {k: v for k, v in src_dict}
  • setdefault(k, default).xxxx(),如果k不在映射之中,就把{k: default}放入字典,并返回default的引用
if key not in my_dict:
    my_dict[key] = []
my_dict[key].append(new_value)

  • collections.defaultdict(my_executor),defaultdict中的参数是一个可调用对象,在__getitem__发生keyError时,defaultdict会调用被赋值给实例default_factory属性的my_executor
  • 所有映射类型,在__getitem__找不到键的时候都会自动调用__missing__方法,而不是抛出KeyError异常
  • 但是__missing__的存在,对__contains__或get不会有任何影响
  • 在python3中运用in运算符会递归调用__contains__函数,而__contains__函数返回的是一个高效的内存视图,不像python2中返回一个列表,然后逐个比对
  • collections.Counter()是一个对k进行计数的字典
  • a = {}创建的是一个空字典,如果想创建空集合,必须使用a = set()

散列表

  • 散列表的单元是表元(bucket),python的装填银子一般为2/3
  • 如果一个整形对象能被存进一个机器字之中,那么它的散列值就是它本身的值
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UR8ik9tS-1580538431162)(C:\Users\Mr.Robot\Desktop\workspace\python_study\hash.png)]

ch-9 符合python风格的对象

  • repr一般得到实例的构造方法表示
  • print会调用str函数
  • {0.mass:5.3e} :左边的表示字段名,右侧部分为格式说明符,最后一位为格式码,传入format_spec的最后一个字符
  • 使用两个前导下划线,将分量标记为私有,并用@property装饰器把读值方法标记为 特性
    • 此时读值方法与公开属性同名
    • 对于以 __property形式命名的实例属性,Python会修改为_className__property,并且把属性名存入实例的__dict__属性中
    • 这个特性称为 名称改写
def property_name(self):return self.__property

  • python默认会在各实例中名为__dict__的字典里存储实例属性,为了使用底层的散列表提升访问速度,字典会消耗大量内存

  • 使用__slots__属性的目的是告诉解释器“这个类中的所有实例属性都在这儿了”,这样python会在各个实例中使用类似元组的结构存储实例变量

  • 如果为不存在的实例属性赋值,同名类属性不受影响,而是在实例中创建新属性,这样之后,self.property读取的是该实例中的新属性,而非类中的同名属性

  • 子类会_继承_父类的所有_类属性_

ch-16 协程

  • 只有协程处于暂停状态下,才可以调用send方法
  • 预激协程
    • next(my_coroutine)
    • my_coroutine.send(None)
  • 当协程因未处理异常而return时,其return的内容会默认赋值给异常类的.value属性
    • 就像for循环迭代一样,会自动捕获StopIteration异常

yield from

  • 在gen中使用yield from subgen()时
    • subgen会获取控制权,并把产出的值传给gen的调用方
    • 即yield from就好像一个管道,让调用方直接
  • yield from x
    • 获取iter(x)
    • 将职责_委托_给子生成器
    • 打开双向通道,把最外层的调用方与最内层的子生成器连接起来,即_委托职责_

python 多线程

多线程

  • threading.Thread(target=,args=())
  • class myThread(threading.Thread)

线程通信

  • queue.Queue本身是线程安全的
  • Queue方式即OS中所讲的共享数据结构方式

线程同步

  • threading.Lock()
    • lock.acquire()
    • lock.release()
  • threading.Condition()
    • condition.wait()
    • condition.notify()
  • threading.Semaphore(size)
    • semaphore.acquire()
    • semaphore.release()

concurrent.futures.ThreadPoolExecutor

  • executor = ThreadPoolExecutor(max_workers=)
    • task = executor.submit(func, (args))
    • task.done()
    • task.cancel()
    • task.result()
    • wait(all_tasks, )
  • 可直接通过executor的map方法获取已经完成的task的值
    • for result in executor.map(my_func, args)
  • concurrent.futures.as_completed
for future in as_completed(all_tasks):
        - 此处为所有已完成task

多进程编程

由于GIL的存在,CPU频繁型操作,使用多进程编程

  • multiprocessing
    • process = multiprocess.Process(target=my_func, args=())
    • process.pid
    • process.start
    • process.join
  • multiprocessing.Pool()线程池
import multiprocessing
import time


def get_html(n):
    time.sleep(n)
    print("sub progress success")
    return n


pool = multiprocessing.Pool(multiprocessing.cpu_count())

# 当pool中进程完成后,pool以进程的结果为参数,调用my_callback_func函数
result = pool.apply_async(get_html, args=(3, ), callback=my_callback_func)
# pool不再接收新进程
pool.close()
# 等待pool中所有进程执行结束
pool.join()

print(result.get())


for result in pool.imap(get_html, [1, 5, 3]):
    print("{} sleep success".format(result))

for result in pool.imap_unordered(get_html, [1, 5, 3]):
    print("{} sleep success".format(result))

  • ProcessPoolExecutor()用法与ThreadPoolExecutor类似

多进程通信

  • queue.Queue不再适用,要改用multiprocessing.Queue
  • 共享变量不再适用于多进程,因为fork子线程时,OS将父进程中所有变量进行值拷贝
  • 而queue.Queue仍不能适用于Pool中的多进程通信,要改用Manager().Queue()
  • 采用Pipe进行多进程通信
from multiprocessing import Pipe

receive_pipe, send_pipe = Pipe()

send_pipe.send("information")
receive_pipe.recv()
# pipe性能优于Manager().Queue()

  • 其他进程通信方式
    • 共享内存方式
    • Array
    • Value
    • dict
    • list

数学运算

  • 在交互模式中,最近的一次数学运算的结果赋值给解释器系统内部的 _ 变量,但是如果自己定义了一个名为 _ 的变量,那么系统内部的 _ 变量就会被覆盖

函数

默认参数问题

def add_end(L=[]):
    L.append('END')
    return L
    
>>> add_end()
['END']
>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']
  • 可以理解为:
    • Python函数在定义的时候,默认参数L的值就被计算出来了,即[],
    • 因为默认参数L也是一个变量,它指向对象[],每次调用该函数,如果改变了L的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]了。

可变参数

  • 参数可变
def my_variables(x, *args):
    print(x)
    print(args)


my_variables(1, 2, 3, 5, 6)
>>>1
(2, 3, 5, 6)

my_variables(1)
>>>1
()
  • 将list转为默认参数传入函数:
a = [1, 2, 3, 5, 6]

my_variables(*a)
>>>1
(2, 3, 5, 6)

关键字参数

  • 可变关键字参数
def my_variable(x, y, **kwargs):
    kwargs["job"] = "CTO"
    print(x, y, kwargs)


my_variable(1, 2, name="刘震", gender="男")
>>>1 2 {'job': 'CTO', 'gender': '男', 'name': '刘震'}
  • 将dict转为关键字参数传入
    • 主义函数内部只是获得了关键字参数字典的一个拷贝,函数内部对该字典的修改不影响外部的字典

personal_info = {"name": "刘震", "gender": "男"}
my_variable(1, 2, **personal_info)
print(personal_info)

命名关键字参数

def limit_kw(name, gender, *, location, job):
    print("limit_kw:=====" + name, gender, location, job)


limit_kw("刘震", "男", location="南京", job="CTO")

高级特性

切片

迭代

  • 迭代字典
    • 迭代key(注意dict内部无序存储,迭代顺序可能跟打印顺序不同)
    for key in d:
    	print(key, d[key])
    
    • 迭代item
    for k, v in d.items():
    	print(k, v)
    
    • 迭代value:
    for v in d.values():
    
    • 同时迭代下标索引和值:
    L = ['a', 'b', 'c', 'd', 'e']
    for i, value in enumerate(L):
    	print(i, value)
    

列表生成式

L1 = ['Hello', 'World', 18, 'Apple', None]
L2 = [s.lower() for s in L1 if isinstance(s, str)]

高阶函数

map

map(f, [1, 2, 3, 4]) = Iterator(f(L[0]), f(L[1]), f(L[2]), f(L[3]))

reduce

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

应用

### str转int
def fn(x, y):
	return x * 10 + y


def char2num(s):
	digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
	return digits[s]

reduce(fn, map(char2num, '13579'))
>>> 13579

### 整理
def str2int(s):
    digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
    def fn(x, y):
        return x * 10 + y
    def char2num(c):
        return digits[c]
    return reduce(fn, map(char2num, s))


### 运用lambda表达式化简
def str2int(s):
    digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
    def char2num(c):
        return digits[c]
    return reduce(lambda x, y: x * 10 + y, map(char2num, s))

闭包

注意到返回的函数在其定义内部引用了局部变量args,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,所以,闭包用起来简单,实现起来可不容易。

另一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行。我们来看一个例子:

def count():
	fs = []
	for i in range(1, 4):
		def f():
			return i*i
		fs.append(f)
	return fs

f1, f2, f3 = count()

在上面的例子中,每次循环,都创建了一个新的函数,然后,把创建的3个函数都返回了。

你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果是:

>>> f1()
9
>>> f2()
9
>>> f3()
9

全部都是9!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。

偏函数

import functools
int2 = functools.partial(int, base=2)
  • 把一个函数的某些参数给固定住(设置默认值),返回一个新函数,调用这个新函数会更简单。
  • 注意到上面的新的int2函数,仅仅是把base参数重新设定默认值为2,但int2仍然可以传入base参数

模块

模块

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

' a test module '					# 模块的文档注释,任何模块代码的第一个字符串都被视为模块的文档注释;

__author__ = 'Michael Liao'

import sys			# 导入sys模块后,我们就有了变量sys指向该模块
				# 利用sys这个变量,就可以访问sys模块的所有功能
				# sys模块有一个argv变量,用list存储了命令行的所有参数
				# argv至少有一个元素,因为第一个参数永远是该.py文件的名称

def test():
    args = sys.argv
    if len(args)==1:
        print('Hello, world!')
    elif len(args)==2:
        print('Hello, %s!' % args[1])
    else:
        print('Too many arguments!')

if __name__=='__main__':
    test()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值