关于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
注: -c 代表自动断点续传
管理环境命令,建议直接写入~/.bashrc中,这样开启terminal后会自动进入py385环境
conda create -n py385 python=3.8 #创建环境
conda activate py385 #激活环境
conda remove -n py385 --all #删除环境
conda config --show #查看conda配置
建立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/
conda管理package
conda list
conda install xxx
conda install xxx=版本号
conda update xxx
conda remove xxx
pip管理package
pip list
pip install xxx
pip uninstall xxx
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. 几种基本数据类型及定义方式
| 类型 | 举例 | 获取元素 |
|---|---|---|
| list | x=[1,2,3] | x[0] |
| tuple | x=(1,2,3) | x[2] |
| dict | x={‘a’:1,‘b’:2,‘c’:3} | x[‘a’]或x.get(‘a’,-1),后者不存在则返回-1 |
| set | x=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)
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)
- 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)]
- 函数还可以作为返回值返回,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
- 把@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
- 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()函数,会输出额外的行
- 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) #两种方法都可得到'中文'
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'}
- 为了和其他语言交互,最好是将对象序列化为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()
- 进程间通信: 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.')
# 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.')
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
- 双向列表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})
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 #注意使用时要将-换成_
...
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']
26. socket编程
HTTP格式:
| 分类 | HTTP Request |
|---|---|
| Request Line | Method sp URL sp Version cr cl |
| Header Lines | Header Name : sp Value cr cl |
| … | |
| Header Name : sp Value cr cl | |
| Blank Lines | cr cl |
| Body Lines | Variable 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 Line | Version sp Status Code sp Phrase cr cl |
| Header Lines | Header Name : sp Value cr cl |
| … | |
| Header Name : sp Value cr cl | |
| Blank Lines | cr cl |
| Body Lines | Variable 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)
- 服务端: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()
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()
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()
- 服务端: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) #两个参数,第一个是数据,第二个是客户端地址与端口
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()
28. 异步IO
- 执行如下一种消息循环
loop = get_event_loop()
while True:
event = loop.get_event()
process_event(event)
- 协程,又称微线程、纤程,其本质特征是执行切换后执行权仍在用户手中,无需内核参与,这样既节省切换开销,又使执行顺序可控
- 协程的底层实现机制是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()
29. WEB开发
- python下有众多web框架可用,flask,sanic,aiohttp等
本文详细介绍了Python的安装配置、执行方式、数据类型、函数式编程、装饰器等核心概念,同时还包括了异常处理、文件操作、多线程编程等内容。
6万+

被折叠的 条评论
为什么被折叠?



