当前位置:   article > 正文

关于Python应该知道的那些事儿_~/.conda/envs/py38/lib/python3.8/codeop.py in __ca

~/.conda/envs/py38/lib/python3.8/codeop.py in __call__(self, source, filenam

关于Python应该知道的那些事儿

1. 安装

wget -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-latest-Linux-x86\_64.sh
bash Miniconda3-latest-Linux-x86\_64.sh
  • 1
  • 2

注: -c 代表自动断点续传

管理环境命令,建议直接写入~/.bashrc中,这样开启terminal后会自动进入py385环境

conda create -n py385 python=3.8 #创建环境
conda activate py385             #激活环境
conda remove -n py385 --all      #删除环境
conda config --show              #查看conda配置
  • 1
  • 2
  • 3
  • 4

建立conda环境,建议根据所安装python版本命名,比如所安装python为python3.8.5
注: conda会建立新目录~/.conda/envs/py385,并在其中建立一份独立的python可执行程序,与系统自带的/usr/bin/下那份无关

添加国内镜像

conda config --add channels  https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
conda config --add channels  https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels  https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/
conda config --add channels  https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/fastai/
conda config --add channels  https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/
conda config --add channels  https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/bioconda/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

conda管理package

conda list
conda install xxx
conda install xxx=版本号
conda update xxx
conda remove xxx
  • 1
  • 2
  • 3
  • 4
  • 5

pip管理package

pip list
pip install xxx
pip uninstall xxx
  • 1
  • 2
  • 3

2. python的几种执行方式

  • 文件首行加#! ~/.conda/envs/py385/bin/python
  • python xxx.py
  • python -m xxx.py

3. python执行后的几个管理工作

  • 查看搜索路径:import sys; print(sys.path)
  • 临时增加搜索路径:sys.path.append(’…’)
  • 永久增加搜索路径:设置环境变量PYTHONPATH,只需要添加自己的搜索路径,Python自己本身的搜索路径不受影响。
  • 查看可执行文件路径:import sys; print(sys.executable)
  • 获取命令行参数:import sys; print(sys.argv)
  • 获取当前路径:import os; print(os.path.abspath(’.’))
  • 获取当前当前模块文件名:print(__file__)

4. 几种基本数据类型及定义方式

类型举例获取元素
listx=[1,2,3]x[0]
tuplex=(1,2,3)x[2]
dictx={‘a’:1,‘b’:2,‘c’:3}x[‘a’]或x.get(‘a’,-1),后者不存在则返回-1
setx=set(list(x))list(x),需要转换为list才可以访问其中元素

list获取偶数项元素: x[::2]
list获取奇数项元素: x[1::2]
list倒序: x[::-1]

如果列表元素是简单类型,去重可用set()
如果列表元素是复合类型,去重可用dict结构,因为key不会重复

xdict={}
for x in xlist:
   xdict.setdefault(x[0],[]).append(x)	
  • 1
  • 2
  • 3

5. iterable、generator、iterator到底啥关系

  • iterable不是形容词,在Python中叫iterable对象,定义了__iter__方法,能用for循环遍历
  • list、set、tuple、str、dict这些都属于container,而container一般都是iterable对象,所以他们都能用for遍历
  • 这些container会一次性把内存分配完,为了节省内存,搞出了iterator对象,特点是用到具体元素时临时生成,也称为惰性序列
  • iterator对象用next()获取元素
  • 用iter()方法可将iterable对象变成iterator对象,也可以通过list()方法将iterator对象变成iterable对象,但是用list()之后原来的iterator对象就变成空了
  • generator是某种iterator,所以也可以执行next()方法,区别在于iterator对象只能输出,generator还能接受输入
  • 有两种generator,一种是genertor expression,就是把list中的[]换成(),另外一种是generator function
  • 包含yield表达式的函数就是generator function,比如x = yield y这样
  • 生成器函数的执行包含两步骤,一是定义,比如a=f(),此时不执行,只是返回生成器对象;二是执行,可用next(a),或者a.send(b),可将b送给x
  • yield y类似于return y,把执行权从yield处返回给调用者,不同在于函数自身会停留在yield处,当下次执行next(a)时,会继续从yield处向下执行,而不是从头开始;如果是a.send(b),那么还会将b送给yield前面的变量,然后继续向下执行。
  • yield就是放弃执行的意思,类似于内核中reschedule,生成coroutine就靠它

6. 啥叫函数式编程和高阶函数

  • 也是一种编程范式,特点就是允许把函数当做参数传给另外一个函数,还能返回一个函数
  • 高阶函数,High-order function,从语法角度上看没啥特别要添加的,就是python内部实现了函数式编程,允许把函数当参数和返回数
  • map()需要接受2个参数,第一个参数是函数f,第二个参数Iterable对象x,作用就是将f依次作用于x每个元素,并将结果作为新的iterator返回
  • reduce()也需要接受2个参数,第一个参数是函数f,第二个参数Iterable对象x,但是f必须接收2个参数,作用如下,返回值是标量,必须使用from functools import reduce导入
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1,x2),x3),x4)
  • 1
  • filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
  • sorted()可以直接对list进行排序,也可接受一个key函数实现自定义的排序,比如按绝对值排序。如果想反向排序,用另一个参数reverse=True
>>> sorted([36, 5, -12, 9, -21],key=abs)
[5, 9, -12, -21, 36]
>>> L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
def by_score(t):
   return(t[1])
>>> L2 = sorted(L, key=by_score)
[('Bart', 66), ('Bob', 75), ('Lisa', 88), ('Adam', 92)] 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 函数还可以作为返回值返回,f=g(),需要注意的是此时f不会执行,当调用f()时才会执行。

7. Lambda函数有什么用

  • 又叫匿名函数,形如lambda [参数列表]: 表达式,列表不加小括号,无参就不写参数;也不需要return,表达式的值就是函数返回值;表达式中不能出现等号。
  • 只能写在一行,所以也叫单行函数
  • 既然函数可以作为别的函数的参数,那写的时候就必须找个地方先定义这个函数,再把名字传进来。那如果函数功能特别简单,那就没必要非得在外面找个地方去定义,直接在调用的地方写Lambda表达式就好了,所以就是为了省事。

8. 装饰器是个什么鬼

  • 一言以蔽之,就是为了扩充被调用函数的功能
def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper

@log
def now():
    print('hello')

>>> now()
call now():
hello
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 把@log放到now()的定义处,相当于执行了语句now=log(now),所以在后面调用now()时,实质上调用的是log(now),也就是先执行wrapper里的print,然后再通过return func执行原来的now()

9. 偏函数有什么用

  • 既然函数f作为g的参数传进去了,那f自己的参数怎么办?答案就是也要作为g的参数传进去,然后由g负责将参数再传给f,比如def f(x), def g(f,y): return f(y)这样。
  • 问题是如果f的参数比较长,然后用的时候参数又比较固定,那么我调用的时候就不想每次都写那么多字,于是引入偏函数,就是把某些参数固定住(也就是设成默认值),返回一个新的函数,调用这个新函数会更简单。
  • 方法是max2 = functools.partial(max,10),以后调用max2()时就不用再提供2这个参数了。
  • 这就是所谓柯里化,以数学家Haskell Curry命名

10. if __name__ == ‘__main__’

  • python文件既可以直接执行,也可以作为模块被别人导入
  • 直接执行时,__name__变量就是__main__,这样就可以执行if下面的语句;而该文件被别人作为模块import时,if条件不满足,所以下面语句不会执行。

11. 如何判断某个实例是某个类型

  • isinstance(a,list)方法,返回True或False
  • 不知道类型名称时怎么办?用type()方法
  • 获取对象所有属性和方法,用dir()方法
  • getattr(obj,‘y’)获取obj对象属性y的值,等同于obj.y
  • setattr(obj,‘y’,19)将obj对象属性y的值设为19
  • hasattr(obj,‘y’)判断obj对象有属性y吗

12. @property的作用

  • 将类中方法当成属性来用,相当于getter方法
  • 如果还允许设置属性,需要配合另一个装饰器@xxx.setter来用,xxx代表方法名
class Student(object):

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • s=Student(); s.score = 60调用@score.setter下面那个函数, print(s.score)调用@property下面那个函数

13. try-except-finally和with异常处理

  • try中代码块有错,会抛出异常,如果被except捕获,则执行except代码块,最后执行finally代码块
  • try中代码块无错,则执行完try代码块后执行finally代码块
  • finally可以没有
  • with后面的语句会被求值,返回对象的__enter__方法被调用,这个方法的返回值会被赋给as后面变量;当with代码块被全部执行完以后,将调用返回对象的__exit__方法。with语句关键之处就在于被求值对象必须有__enter__方法和__exit__方法,然后就可以利用这两个方法处理异常。

14. 调试四法

  • print大法,坏处是删掉时麻烦
  • assert大法,assert n!=0, ‘n is zero’,意思是如果n==0则抛出异常AssertionError,比print唯一好一点的地方是可以在执行python解释权时用-O关闭assert
  • logging大法,需要import logging,好处是可以指定信息的级别,比如logging.basicConfig(level=logging.INFO)。有debug,info,warning,error等几个级别,当指定level=INFO时,logging.debug就不起作用了。同理,指定level=WARNING后,debug和info就不起作用了。这样一来,你可以放心地输出不同级别的信息,也不用删除,最后统一控制输出哪个级别的信息。
  • pdb大法。python -m pdb xxx.py

15. 读写文件

  • open默认读取UTF-8编码的文本文件,若要读取二进制文件,要用’rb’模式
  • x=f.read(),全部内容一次性读完, 以字符为单位存放
  • x=f.readline(), 一次读取一行, 以字符为单位存放, 通常需配合循环语句使用
with open(filename,'r') as f:
    while True:
        line = f.readline()
        if line == '':
             break
        print(line.strip()) #如果不用strip()函数,会输出额外的行
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • x=f.readlines(),一次全读完,以行为单位存放
  • 写文件用f.write()
  • 把内存中数据当成文件一样读写,用StringIO和ByteIO,前者读写str,后者操作Bytes,且都需要从io中导入
from io import StringIO, BytesIO
f = StringIO()
g = StringIO('Hello!\nHi!\nGoodbye!')
while True: # 如同操作文件一样
   s=g.readline()
   if s == '':
      break	
   f.write(s.strip())
print(f.getvalue()) # getvalue()方法用于获得写入后的str

h = BytesIO()
h.write('中文'.encode('utf-8'))
print(h.getvalue()) #输出为b'\xe4\xb8\xad\xe6\x96\x87'

k = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
m = k.read()
print(str(m, encoding="utf-8")
bytes.decode(m) #两种方法都可得到'中文'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

16. 如何使用操作系统提供的命令

  • 使用import os, 可使用os.name, os.environ, os.environ.get(‘PATH’), os.uname(), os.path.join(), os.mkdir(), os.path.split(), os.path.splittext(), os.rename(), os.remove(), os.path.isdir(), os.listdir(), os.path.isfile()
  • 执行os.system(‘ls -l test.db’),成功则返回0

17. 序列化

  • 把变量从内存中变成可存储或传输的过程叫序列化,python中叫pickling,使用pickle模块
  • 其他语言称之为serialization, marshalling, flattening
  • 把变量内容从序列化对象重新读到内存称之为反序列化,即unpickling
>>> import pickle
>>> d = dict(name='Bob', age=20, score=88)
>>> pickle.dumps(d)           
b'\x80\x03}q\x00(X\x03\x00\x00\x00ageq\x01K\x14X\x05\x00\x00\x00scoreq\x02KXX\x04\x00\x00\x00nameq\x03X\x03\x00\x00\x00Bobq\x04u.'
>>> f = open('dump.txt','wb') #将dumps内容写入文件,注意前面使用的是dumps(),这里使用的是dump()
>>> pickle.dump(d,f)
>>> f.close()
>>> g = open('dump.txt','rb') #将dumps内容读回来
>>> h = pickle.load(g)
>>> g.close()
>>> h
{'age': 20, 'score': 88, 'name': 'Bob'}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 为了和其他语言交互,最好是将对象序列化为JSON格式,使用json模块,即使用json.dump/dumps/load/loads方法

18. 进程、线程、协程

  • 一个程序的执行就是一个进程,如果这个进程因为IO操作而阻塞(这个阻塞是内核在系统调用实现时强制实现的,所谓阻塞就是置进程状态为TASK_INTERRUPTABLE,然后执行reshedule()函数调用其他进程执行),那么整个程序其他部分也都得不到执行
  • 如果这个进程有多个线程,那么IO阻塞只会影响到读写IO那个线程,从操作系统角度看虽然还是要重新调度,但还是有机会调度得到该进程的其他线程的,所以其他线程不会空等
  • 但是线程调度还是在内核层面进行的,程序员不可控,而且创建线程开销也比较大,因此引入协程,就是让程序员自己可以控制调度,所有协程都在一个线程内,不涉及内核,所以开销比较小
  • 协程的本质就是用户级线程
  • 不论是线程还是协程,都有对应的yield操作,即主动放弃执行权,区别在于放弃之后执行权交给谁的问题,前者交给OS,后者交个用户自己
  • 进程:
from multiprocessing import Process
import os

# 子进程要执行的代码
def run_proc(name):
    print('Run child process %s (%s)...' % (name, os.getpid())) # 获取进程号用os命令getpid

if __name__=='__main__':
    print('Parent process %s.' % os.getpid())
    p = Process(target=run_proc, args=('test',)) # Process()创建进程,注意如何指定子进程要执行的函数和对应参数
    print('Child process will start.')
    p.start() # 子进程开始执行
    p.join() # 等待子进程结束,用于进程间同步,强制进程结束用p.terminate(),比如子进程执行死循环
    print('Child process end.')

# 进程池,用于启动大量子进程情况
from multiprocessing import Pool
    ...
    p = Pool(4)                            # 这里是声明进程池
    for i in range(5):
        p.apply_async(run_proc, args=(i,)) # 这里指定子进程执行函数及参数,随后即进入调度队列
    p.close()                              # 调用join前要关闭进程池
    p.join()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 进程间通信: from multiprocessing import Queue, Pipes; q = Queue(); q.put(value); q.get(True)

19. 分布式进程

  • 让进程跑在不同的机器上,通过multiprocessing.managers模块实现,就是通过managers把Queue通过网络暴露出去
  • 可以实现一种master-worker工作模式,基本流程是:
    • master分配queue,然后注册在manager上,为manager设置监听端口和验证码,启动manager,通过manager重新获取queue,向输出queue执行put操作,向输入queue执行get操作,输入queue为空则会等待在那里,最后关闭manager;
    • worker首先也要向manager注册与master同名接口的queue,然后为manager绑定与master一致的监听端口和验证码,启动manger连接操作,通过manager获取queue,从输入队列get数据,向输出队列put数据,最后退出即可
# task_master.py,要先启动master再启动worker,不然worker的连接请求会被拒绝

import random, time, queue
from multiprocessing.managers import BaseManager

# 由master分配发送任务队列和接收结果队列:
task_queue = queue.Queue()
result_queue = queue.Queue()

# 从BaseManager继承的QueueManager:
class QueueManager(BaseManager):
    pass

# 把两个Queue都注册到网络上, callable参数关联了Queue对象:
# 相当于给队列重新起了别名,这样后续对队列读写都必须通过QueueManager对象提供的接口实现
QueueManager.register('get_task_queue', callable=lambda: task_queue)
QueueManager.register('get_result_queue', callable=lambda: result_queue)
# 绑定端口5000, 设置验证码'abc':
manager = QueueManager(address=('', 5000), authkey=b'abc')
# 启动Queue:
manager.start()

# 获得通过网络访问的Queue对象:
# 对比前面的task_queue和result_queue,这里已经是通过QueueManager操作队列了
task = manager.get_task_queue()
result = manager.get_result_queue()

# 放几个任务进去:
for i in range(10):
    n = random.randint(0, 10000)
    print('Put task %d...' % n)
    task.put(n)

# 从result队列读取结果:
print('Try get results...')
# 队列没有结果会停在这里
for i in range(10):
    r = result.get(timeout=10)
    print('Result: %s' % r)

# 关闭:
manager.shutdown()
print('master exit.')
  • 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
# task_worker.py

import time, sys, queue
from multiprocessing.managers import BaseManager

# 创建类似的QueueManager:
class QueueManager(BaseManager):
    pass

# 由于这个QueueManager只从网络上获取Queue,所以注册时只提供名字:
QueueManager.register('get_task_queue')
QueueManager.register('get_result_queue')

# 连接到服务器,也就是运行task_master.py的机器:
# 如果是本机运行就设为127.0.0.1
server_addr = '172.31.226.31'
print('Connect to server %s...' % server_addr)
# 端口和验证码注意保持与task_master.py设置的完全一致:
m = QueueManager(address=(server_addr, 5000), authkey=b'abc')
# 从网络连接:
# 如果master未启动,这里会抛出异常
m.connect()
# 获取Queue的对象:
task = m.get_task_queue()
result = m.get_result_queue()
# 从task队列取任务,并把结果写入result队列:
for i in range(10):
    try:
        n = task.get(timeout=1)
        print('run task %d * %d...' % (n, n))
        r = '%d * %d = %d' % (n, n, n*n)
        time.sleep(1)
        result.put(r)
    except Queue.Empty:
        print('task queue is empty.')
# 处理结束:
print('worker exit.')
  • 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

20. 多线程

  • 相比于进程,线程只独占stack和CPU寄存器,与其他线程共享主进程内存和文件描述符等,因此线程间通信更方便,所占据资源也更少
  • 创建线程需要import _thread或threading,后者是对前者的封装,因此最好使用后者
  • 线程的创建、启动、等待与进程类似,import threading,t=threading.Thread(target,args)负责创建,t.start()负责启动,t.join()负责等待
  • 多线程对共享变量的保护要通过lock=threading.Lock()和lock.acquire()/lock.release()实现
  • Python解释器有个GIL锁,是对“解释器”上的锁,任何线程执行前必须先获得GIL锁,然后每执行100条字节码解释器自动释放GIL锁,让别的线程也有机会执行。因此多线程在Python中也只能交替执行。即使有100个线程跑在100核CPU上,也只能用到1个核
  • 通常使用的Python解释器是CPython,要真正利用多核,除非重写一个不带GIL的解释器。

21. 正则表达式

  • 用re模块,re.match(r’’,str)进行匹配,re.sub(r’’,str)进行替换,用r来使后面的表达式保持原样
  • 非贪婪匹配是在表达式后面加个?,比如\d+?
  • 所谓分组groups,就是提取子串,表达式内部用()标识分组,m=re.match(r’()()’,str),m.group(0)是原始str,m.group(1)是第一个分组即第一个()对应子串,m.group(2)是第二个,依次类推; m.groups()则直接返回从m.group(1)到最后组成的tuple
  • 可以先编译正则表达式,然后再使用编译后的结果,以提升效率,re_cmpl = re.compile(r’’),然后直接使用re_cmpl.match(str)

22. 记录和使用时间

  • 探索一个函数执行时间,可使用imort time,然后在其执行前后加上start=time.time()和run_time=time.time()-start得到,精度是秒,即小数点统计到秒。
  • import datetime可以得到更丰富信息
    • 微秒:datetime.datetime.now().microsecond
    • 日期:datetime.date.today()

23. 使用collections大杂烩

  • 有名元组namedtuple:体现在一是分配对象时用名字,不再是单一的(), 二是使用元组中元素时,用.+名方式,不再是[]+下标
from collections import namedtuple
Point = namedtuple('Point',['x','y'])
p=Point(x,y)
p.x,p.y
  • 1
  • 2
  • 3
  • 4
  • 双向列表deque:弥补原生list插入和删除速度慢的不足,q=deque([‘a’,‘b’,‘c’]),q.append/appendleft/pop/popleft,可实现头尾两端插入与删除元素
  • defaultdict:希望引用字典的key而key不存在时不抛出KeyError而是返回默认值,其实用get()方法也是可以的,只是用defaultdict定义的字典,能用d.[key]访问而不报错,但定义时要用lambda函数d=defaultdict(lambda: ‘N/A’)
  • OrderedDict:可保证遍历时得到key的顺序与字典生成或插入时顺序一致
  • Counter:可以统计字符出现的个数
>>> from collections import Counter
>>> c = Counter()
>>> for ch in 'programming':
...     c[ch] = c[ch] + 1
...
>>> c
Counter({'g': 2, 'm': 2, 'r': 2, 'a': 1, 'i': 1, 'o': 1, 'n': 1, 'p': 1})
>>> c.update('hello') # 也可以一次性update
>>> c
Counter({'r': 2, 'o': 2, 'g': 2, 'm': 2, 'l': 2, 'p': 1, 'a': 1, 'i': 1, 'n': 1, 'h': 1, 'e': 1})

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

24. 解析命令行参数

  • 使用argparse,先定义一个parser=argparse.ArgumentParser(),再对每个参数逐一调用parser.add_argument(),最后使用cli_args=parser.parse_args(),然后就可以使用cli_args.para_name使用各参数
class ClassificationTrainingApp:
    def __init__(self, sys_argv=None):
        if sys_argv is None:
            sys_argv = sys.argv[1:]

        parser = argparse.ArgumentParser()
        parser.add_argument('--batch-size',
            help='Batch size to use for training',
            default=24,
            type=int,
        )
        parser.add_argument('--malignant',
            help="Train the model to classify nodules as benign or malignant.",
            action='store_true', #代表一旦有这个参数,就将其值设为True,等于不用另外加值了
            default=False,
        )
        parser.add_argument('comment',
            help="Comment suffix for Tensorboard run.",
            nargs='?', #为?代表如果命令行参数出现comment,则给comment赋以默认值dlwpt
            default='dlwpt',
        )
        ...
        self.cli_args = parser.parse_args(sys_argv) #不加参数默认也是用sys.argv获取

     def initTrain(self):
        batch_size = self.cli_args.batch_size #注意使用时要将-换成_
        ...
  • 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

25. 无限迭代器itertools模块

  • count(start=0,step=1),返回无限自然数
  • cycle(),将参数序列无限循环下去
  • repeat(),将第一个参数无限循环下去,可以用第二个参数指定重复次数
  • takewhile(),根据条件判断来截取一个有限序列,如n=itertools.count(1),ns=itertools.takewhile(lambda x: x<=10, natuals)
  • chain(),把迭代器拼接起来,类似于list中的加法
  • groupby(),把迭代器相邻重复元素挑出来放在一起
>>> for key, group in itertools.groupby('AaaBBbcCAAa', lambda c: c.upper()):
...    print(key, list(group)) # 用lambda函数表示挑选相同元素时忽略大小写
A ['A', 'a', 'a']  # 但返回结果还是原来的样子
B ['B', 'B', 'b']
C ['c', 'C']
A ['A', 'A', 'a']
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

26. socket编程

HTTP格式:

分类HTTP Request
Request LineMethod sp URL sp Version cr cl
Header LinesHeader Name : sp Value cr cl
Header Name : sp Value cr cl
Blank Linescr cl
Body LinesVariable Numbers of Lines
(Present only in some messages)

注:cr cl代表/r/n, sp代表空格
METHOD: GET/HEAD/POST/PUT/TRACE/CONNECT/DELETE/OPTIONS
Header Name: User-agent/Accept/Host/Date/Upgrade/Cookie/If-Modified-Since/…

分类HTTP Response
Status LineVersion sp Status Code sp Phrase cr cl
Header LinesHeader Name : sp Value cr cl
Header Name : sp Value cr cl
Blank Linescr cl
Body LinesVariable Numbers of Lines
(Present only in some messages)

注:Status Code:100/101/200/400/404/…
Phrase: Continue/Switching/OK/Bad request/Not Found/…
Header Name: Server/Set-Cookie/Content-Length/Location/Upgrade/Last-Modified/…

TCP编程-有连接

  • 客户端:socket-connect-send-recv-close
import socket
# 创建socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立连接,参数是个tuple
s.connect(('www.sina.com.cn',80))
# 发送数据
s.send(b'GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')
# 接收数据
buffer = []
while True:
   # 每次最多接收1k字节
   d = s.recv(1024)
   if d:
      buffer.append(d)
   else:
      break
data = b''.join(buffer)
# 关闭连接
s.close()
# 分离HTTP header、HTTP body
header,html = data.split(b'\r\n\r\n',1)
# 打印头部
print(header.decode('utf-8'))
# 写入文件
with open('sina.html','wb') as f:
   f.write(html)
  • 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
  • 服务端:socket-bind-listen-accept-send-recv-close
# server端程序
import socket
import threading
import time

def tcplink(sock, addr):
   sock.send(b'Welcome!')
   while True:
      data=sock.recv(1024)
      time.sleep(1)
      if not data or data.decode('utf-8') == 'exit':
          break
      sock.send(('Hello, %s!' % data.decode('utf-8')).encode('utf-8'))
   sock.close()
   print('Connection from %s:%s closed.' % addr)

# 创建socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定端口
s.bind(('0.0.0.0',9999))
# 监听端口,参数指定等待连接的最大数量
s.listen(5)
print("Waiting for connection...")
# 永久循环接受客户端连接
# 每个连接都必须创建新线程(或进程处理),否则处理连接过程中无法接受其他客户端连接
while True:
   # 接受一个新连接:
   sock, addr = s.accept()
   # 创建新线程来处理TCP连接:
   t = threading.Thread(target=tcplink, args=(sock, addr))
   t.start()
  • 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
import socket
import os
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立连接:
s.connect(('172.31.226.31', 9999))
# 接收欢迎消息:
print(s.recv(1024).decode('utf-8')) #注意这里是如何将bytes转换为str的

for data in ['Michael', 'Tracy', 'Sarah']:
    # 发送数据:
    data=data+'_'+str(os.getpid())
    data=bytes(data,encoding='utf-8') #注意这里是如何将str转换为bytes的
    s.send(data)
    print(s.recv(1024).decode('utf-8'))
s.send(b'exit') #注意这里的字节对象定义方式
s.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

UDP编程-无连接

  • 客户端:socket-sendto-recv-close
import socket
# SOCK_DGRAM表明这是udp类型
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for data in [b'Michael', b'Tracy', b'Sarah']:
   # 发送数据:
   s.sendto(data, ('172.31.226.31', 9999))
   # 接收数据:
   print(s.recv(1024).decode('utf-8'))
s.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 服务端:socket-bind-recvfrom-sendto-close
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定端口:
s.bind(('0.0.0.0', 9999))
print('Bind UDP on 9999...')
while True:
   # 接收数据,recvfrom返回数据和客户端地址与端口,这样就可以直接通过sendto就可以发送数据出去
   data, addr = s.recvfrom(1024)
   # addr是tuple
   print('Received from %s:%s.' % addr)
   s.sendto(b'Hello, %s!' % data, addr) #两个参数,第一个是数据,第二个是客户端地址与端口
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

27. 访问数据库

  • 内置sqlite3,它的数据库就是一个文件,轻量级,不能承受高并发访问,适合桌面和移动应用,不适合服务器
  • 要操作数据库首先需要连接数据库,使用conn=sqlite3.connect(‘name.db’)
  • 连接到数据库后需要打开游标,使用cursor=conn.cursor()
  • 使用cursor执行SQL语句cursor.execute(‘create …’)
  • 关闭cursor,cursor.close()
  • 提交transaction,conn.commit()
  • 关闭connection,conn.close()
  • ORM: Object-relational Mapping,把关系数据库的表结构映射到对象上,像访问类对象一样访问数据库表,需借助sqlalchemy
import sqlite3
# 开
conn = sqlite3.connect('test.db') #没有会新创建一个
cursor = conn.cursor() #创建一个Cursor
# 增
cursor.execute('create table user (id varchar(20) primary key, name varchar(20))') #创建表
cursor.execute('insert into user (id, name) values (\'1\', \'Michael\')') #插入记录
cursor.execute('insert into user (id, name) values (\'2\', \'Jack\')') #插入记录
cursor.rowcount #获取行数,2
# 查
cursor.execute('select * from user where id=?', ('1',)) #?代表通过参数传入id值
values = cursor.fetchall() #获取查询结果集
print(values) # [('1', 'Michael')]
# 改
cursor.execute('update user set name=\'Alice\' where id=\'1\') #修改记录
cursor.execute('select * from user') #获取所有行
values = cursor.fetchall() #获取查询结果集
print(values) # [('1', 'Alice'),('2','Jack')]
# 删
cursor.execute('delete from user where id=\'1\'') #删除记录
cursor.rowcount #获取行数,1
# 关
cursor.close()
conn.commit()
conn.close()
  • 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

28. 异步IO

  • 执行如下一种消息循环
loop = get_event_loop()
while True:
   event = loop.get_event()
   process_event(event)
  • 1
  • 2
  • 3
  • 4
  • 协程,又称微线程、纤程,其本质特征是执行切换后执行权仍在用户手中,无需内核参与,这样既节省切换开销,又使执行顺序可控
  • 协程的底层实现机制是generator
  • 现代定义方式是用async def替换@asyncio.coroutine,同时用await替换了yield from,基本流程如下
  • yield from是从后面的协程中获取返回结果,赋给前面变量,然后继续向下执行
import asyncio

# 定义协程
async def task():
   ...
   # await后面也必须是协程或异步操作
   await 
   ...

loop = asyncio.get_event_loop()
tasks = [task, task]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

29. WEB开发

  • python下有众多web框架可用,flask,sanic,aiohttp等
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/66497
推荐阅读
相关标签
  

闽ICP备14008679号