Python学习笔记

  写这篇笔记的缘由,首先想系统地学习一下 python,之前也片段地学了下,每次用起的时候,都不太想得起具体的用法,要搜索很久。同时,个人感觉教程内容有点杂,于是总结了下方便查阅,也加深一下印象。

  Python教程——廖雪峰

Python 基础

输入和输出

输出

  • print(),加引号输出字符串,不加输出变量或计算结果
    遇到 , 输出一个空格,print('The quick brown fox','jumps over','the lazy dog')

输入

  • input(),可在加上输入提示参数

数据类型和变量,基本运算

数据类型

  • 整数

  • 浮点数

  • 字符串
    r'' 表示 '' 内的字符默认不转义
    '''...''' 表示多行内容,直接换行,不用 \n

  • 布尔值: True 和 False

  • 空值:None,不能理解为0

变量

  • 必须由大小写字母,数字和下划线组成,且不能以数字开头

基本运算

  • / 除法,精确的,10 / 3 = 3.333333333
  • // 地板除,两个整数的地板除仍为整数, 10 // 3 = 3

字符串和编码

Python字符串

  • Python 3 字符串以 Unicode 编码

  • ord() 获取字符的整数表示,chr()编码转换成对应字符

  • 网络或磁盘上传输的是字节流类型 bytes
    encode() 将字符串转换成指定编码的 bytes '中文'.encode('utf-8')
    decode() 将 bytes 转换成指定编码的数据,传入 errors='ignore' 忽略无法解码的字节 b'\xe4\xb8\xad\xff'.decode('utf-8',errors='ignore')

  • len() 计算字符数,如果传入 bytes ,计算字节数

  • 使 Python 解释器以指定编码读取源代码

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

格式化

  • % 对应 %d, %f, %s, %x(十六进制整数) 'Hi, %s,you have $%d' % ('Michael', 10000);输出单个 %%%

  • format() 'Hello, {0}, 成绩提升了 {1:.1f}%'.format('小明', 17.25)

  • f-strings

list 和 tuple

list 列表

  • [] 包裹,classmates = ['Michael', 'Bob', 'Tracy']

  • len() 获取 list 元素个数

  • 使用索引访问元素,-1 获取最后一个元素

  • append() ,将元素将元素追加到末尾

  • insert() 插到索引号为 1 的位置: classmates.insert(1, 'Jack')

  • pop(i) 删除指定位置的元素

  • 直接给元素赋值替换元素

tuple 元组

  • 一旦初始化就不能改变
  • 使用索引访问元素
  • append()insert() 方法
  • 只有一个元素时添加 , 消除歧义 t = (1,)

list 和 tuple 区别

  • list 可变,tuple 不可变

条件判断

  • ifelse 后面都有 :

  • else if 可用 elif 替代

循环

  • for .. in 循环,for name in names:

  • range() 生成整数序列

  • while 循环

  • continue,break

dict 和 set

dict 字典

  • {} 包裹,names = {'Michael': 95, 'Bob': 70, 'Tracy': 85}

  • 判断 key 存在
    1、in 判断是否存在; 'Thomas' in names
    2、get() 方法,不存在返回 None 或者指定的 value ; names.get('Thomas', -1)

  • pop(key) 删除 key ,对应 value 也删除; names.pop('Bob')

  • 存放顺序和 key 放入的顺序无关,用空间换取时间

  • 作为 key 的对象不可变,如字符串,整数

set

  • 和 dict 类似,key 的集合,不存储 value,没有重复的 key
  • 创建 set,需提供一个 list 作为输入集合; s = set([1, 2, 3])
  • 重复元素自动被过滤
  • add(key) 添加元素,重复添加没有效果; s.add(4)
  • remove(key) 删除元素; s.remove()4
  • set 可做交集,并集操作; s1 & s2s1 | s2

不可变对象

  • 调用自身任意方法,也不会改变对象自身的内容

函数

调用函数

  • 查看函数的名称和参数
    python 官方文档:https://docs.python.org/3/library/functions.html
    通过 help() 查看; help(abs)

  • 参数数量,类型不对,会报 TypeError 错误

定义函数

  • def + 函数名 + 参数名 + 冒号;def my_abs(x):

  • 定义空函数:pass

    1
    2
    def nop():
    pass

函数参数

  • 位置参数:普通参数,调用函数时按照位置顺序依次赋值

  • 默认参数
    在函数中设定默认参数值,调用时可以不写默认参数的值:

1
2
3
4
5
6
7
8
9
def power(x, n=2):
s = 1
while n > 0:
n = n - 1
s = s * x
return s
power(5) #25
power(5, 3) #125
  • 必选参数在前,默认参数在后,默认参数中变化大的参数在前,变化小的在后

  • 多个默认参数时,改变其中某个参数,指明参数名

1
2
3
4
5
6
7
def student(name, gender, age=6, city='Beijing'):
print('name:', name)
print('gender:', gender)
print('age:', age)
print('city:', city)
student('Adam', 'M', city='Tianjin')
  • 默认参数大坑,须指向不变对象

  • 可变参数:*param
    传入参数,得到 list
    函数定义:

    1
    2
    3
    4
    5
    def calc(*numbers):
    sum = 0
    for n in numbers:
    sum = sum + n * n
    return sum

调用函数:

1
calc(1, 2, 3)

传入一个 list 或 tuple(元组),前加 * ,*nums 表示把 nums 这个 list 的所有元素作为可变参数传进去:

1
calc(*nums)

  • 关键字参数: **kw
    传入参数,得到 dict
    函数定义:
    1
    2
    def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)

调用函数:

1
2
>>> person('Bob', 35, city='Beijing')
name: Bob age: 35 other: {'city': 'Beijing'}

传入一个 dict,前加 ** ,**extra 表示把 extra 这个 dict 的所有 key-value 传进去

1
2
3
>>> extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack', 24, **extra)
name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

  • 命名关键字参数
    限制关键字参数的名字,添加分隔符 *, * 之后的为命名关键字参数
    函数定义:
    1
    2
    def person(name, age, *, city, job)
    print(name, age, city, job)

函数调用

1
2
>>> person('Jack', 24, city='Beijing', job='Engineer')
Jack 24 Beijing Engineer

已有可变参数,后面命名关键字参数不需要特殊分隔符:

1
2
def person(name, age, *args, city, job):
print(name, age, args, city, job)

命名关键字参数可以有默认值

递归函数

  • 函数里调用函数自身

高级特征

切片

  • L[0:3] 表示从索引 0 开始,直到索引 3 结束,不包括索引 3

  • 索引是 0 可以省略,L[:3]

  • 'abcdef'[1::2] 从索引 1 开始每隔 2 个取 1 个

  • 负索引也生效

迭代

  • for ... in

  • 迭代 dict ,d 是 dict,for key in d 迭代的是 key 。若要迭代 value ,for value in d.values()。同时迭代 key 和 value,for d,v in d.items()

  • 判断是否可迭代对象,通过 collections 模块的 iterable 类型判断

    1
    2
    3
    4
    5
    6
    7
    >>> from collections import Iterable
    >>> isinstance('abc', Iterable) # str是否可迭代
    True
    >>> isinstance([1,2,3], Iterable) # list是否可迭代
    True
    >>> isinstance(123, Iterable) # 整数是否可迭代
    False
  • enumerate() 函数同时迭代索引和元素本身:

    1
    2
    3
    4
    5
    6
    >>> for i, value in enumerate(['A', 'B', 'C']):
    ... print(i, value)
    ...
    0 A
    1 B
    2 C

列表生成式

  • List Conprehensions
    生成的列表中的内容 + for 迭代

  • 生成[1x1, 2x2, 3x3, …, 10x10]

    1
    2
    >>> [x * x for x in range(1, 11)]
    [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

也可采用循环方式

1
2
3
4
5
6
>>> L = []
>>> for x in range(1, 11):
... L.append(x * x)
...
>>> L
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

  • 加上判断,筛选出偶数的平方

    1
    2
    >>> [x * x for x in range(1, 11) if x % 2 == 0]
    [4, 16, 36, 64, 100]
  • 两层循环,生成全排列

    1
    2
    >>> [m + n for m in 'ABC' for n in 'DEF']
    ['AD', 'AE', 'AF', 'BD', 'BE', 'BF', 'CD', 'CE', 'CF']
  • 可调用列表生成内容的方法
    把 list 中所有字符串转换成小写

    1
    2
    3
    >>> L = ['Hello', 'World', 'IBM', 'Apple']
    >>> [s.lower() for s in L]
    ['hello', 'world', 'ibm', 'apple']

生成器(generator)

  • 节省空间,一边循环一边计算

  • 第一种方法,将列表生成器中 [] 改成 ()

    1
    2
    3
    4
    5
    6
    >>> L = [x * x for x in range(1, 11)]
    >>> L
    [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
    >>> g = (x * x for x in range(1, 11))
    >>> g
    <generator object <genexpr> at 0x0512BE68>

next() 函数获取 generator 的下一个返回值,没有更多元素时,抛出 StopIteration 错误,一般采用迭代的方式获取

1
2
3
4
>>> next(g)
1
>>> next(g)
4

  • geneterator 生成斐波拉契数列
    1
    2
    3
    4
    5
    6
    7
    def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
    yield b
    a, b = b, a + b
    n = n + 1
    return 'done'

函数遇到 return 返回,变成 generator 的函数,每次调用 next() 的时候执行,遇到 yield 返回,再次执行从上次返回的 yield 语句继续执行。

迭代器

nop

函数式编程

nop

模块

  • 一个 .py 文件为一个模块

  • 将函数分组到模块中,提高代码可维护性

  • 通过包名解决模块名冲突的问题
    包名.模块名
    mycompany.abc

使用模块

  • 模块示列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
' a test module '
__author__ = 'Michael Liao'
import sys
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()

第四行为模块文档注释,任何模块第一个字符串都被视为文档注释

第六行用 __author__ 变量写入作者名字

if __name__=='__main__' 用于测试模块功能

  • 作用域
    正常函数和变量名是公开(public)的,如:abc,x123
    __xxx__ 为特殊变量,可以被引用,有特殊函数
    _xxx__xxx 为非公开的(private)

安装第三方模块

python -m pip install xxx

面向对象编程

类和实例

1
2
3
4
5
6
7
8
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def print_score(self):
print('%s: %s' % (self.name, self.score))
  • 类名后括号内为继承的类,默认为 object

  • 构造函数 def __init__(self, name, score)
    第一个参数为 self
    操作类的属性 self.name

  • 内部函数 def print_score(self)
    第一个参数为 self

  • 实例化对象直接调用构造函数,不需要 new

访问限制

  • __ 两个下划线开头,私有变量,不能访问,实际名字改变,如 __name 改成了 _Student__name ,不同 python 版本处理方式不同
  • _ 单下划线开头看做私有变量

  • 私有变量采用 get 和 set 方法 可检查传入参数

继承和多态

  • 被继承的 class 称为基类,父类,或超类(Base class,Super class),继承的 class 称为 子类 (subclass)

  • isinstance() 判断变量是否某个类型

    1
    2
    >>> isinstance(a, list)
    True
  • 子类计时父类型又是子类型,父类不是子类型

获取对象信息

  • type() 获取对象类型

  • isinstance() 判断对象是否属于某个类型

  • dir() 获取对象的所有属性和方法,__xxx__ 的是特殊的属性和方法

  • getattr() 获取属性或方法,setattr() 设置属性或方法,hasattr() 判断属性或方法是否存在

实例属性和类属性

  • 实例属性优先级高于类属性

错误,调试和测试

错误处理

  • 使用返回错误代码的方式,繁琐,调试不方便

  • try...except...finally 错误处理机制,try 可能出错的代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    try:
    print('try...')
    r = 10/0
    print('result=', r)
    except ZeroDivisionError as e:
    print('except:', e)
    finally:
    print('finally...')
    print('END')

执行 r = 10/0 捕获到 ZeroDivisionError,不再执行 try 中之后语句
执行 except 语句
执行 finally 语句
继续执行剩下语句

  • 设置多个 except 语句块捕获多种不同类型的语句
    错误类型也是 class ,存在子类父类,except 父类错误时也将子类’一网打尽’了
    所有错误从 BaseException 类派生
    常见错误类型和错误关系:https://docs.python.org/3/library/exceptions.html#exception-hierarchy

  • 如果没有捕获到错误可加一个 else 语句,没有错误发生,执行 else 中语句

  • finally 语句不管错误是否发生,最后都会执行

  • 调用栈:错误的跟踪信息,异常栈

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $ python3 err.py
    Traceback (most recent call last):
    File "err.py", line 11, in <module>
    main()
    File "err.py", line 9, in main
    bar('0')
    File "err.py", line 6, in bar
    return foo(s) * 2
    File "err.py", line 3, in foo
    return 10 / int(s)
    ZeroDivisionError: division by zero
  • 使用 logging 记录错误信息

  • raise 抛出错误

调试

  • 断言:assert
1
2
3
4
5
6
7
def foo(s):
n = int(s)
assert n != 0, 'n is zero!'
return 10 / n
def main():
foo('0')

assert 断言失败,抛出 AssertionError,python 解释器参数 -o 关闭 assert

  • logging
    控制语句输出到不同的地方,比如 console 和文件
1
2
3
4
5
6
7
import logging
logging.basicConfig(level=logging.INFO)
s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)
  • pdb
    python -m pdb err.py
    1 查看代码
    n 单布执行代码
    p 变量名 查看变量
    q 结束调试
    程序中 pdb.set_trace() 设置断点,命令 c 继续执行

  • IDE调试
    pycharm

单元测试

nop

文档测试

nop

IO编程

数据的输入(input)输出(output)

分为同步和异步,区别为是否等待IO执行的结果,等待的为同步IO

文件读写

  • 现代操作系统不允许普通的程序直接操作磁盘,读写文件请求操作系统打开一个文件对象

  • 读文件
    1.open() 函数传入文件名和标识符 f = open('test.txt', 'r')
    2.read() 一次读取文件的全部内容;read(size) 每次最多读取 size 个字节的内容;readline() 每次读取一行内容;readlines() 读取所有内容并返回 list
    3.close() 文件使用完后必须关闭
    4.使用 try..finally 确保使用完正确关闭文件,使用 with 语句自动调用 close() 方法

    1
    2
    with open('test.txt', 'r') as f:
    print(f.read())

5.二进制文件采用标志符 rb 打开
6.encoding 设置读取文件的编码方式,errors 设置遇到编码错误后的处理方式
f = open('text.txt', 'r', encoding='gbk', errors='ignore')

  • 写文件
    1.传入标识符 'w','wb' 写文本文件或二进制文件
    2.wtite() 写入文件
    3.务必使用 colose() 关闭文件,确保数据全部写入磁盘。写文件时,不是立即写入磁盘,而是在内存中缓存起来,空闲的时候再写。
    4.'w' 如果文件已存在,会直接覆盖,'a' 以追加的模式写入
    标识符含义:https://docs.python.org/3/library/functions.html#open

  • fire-like object
    read() 方法

StringIO 和 BytesIO

在内存中读写 str 和 byte

  • getvalue() 获取写入后的值

StringIO:

1
2
3
4
5
6
7
8
9
10
>>> from io import StringIO
>>> f = StringIO()
>>> f.write('hello')
5
>>> f.write(' ')
1
>>> f.write('world!')
6
>>> print(f.getvalue())
hello world!

1
2
3
4
5
6
7
8
9
10
11
>>> from io import StringIO
>>> f = StringIO('Hello!\nHi!\nGoodbye!')
>>> while True:
... s = f.readline()
... if s == '':
... break
... print(s.strip())
...
Hello!
Hi!
Goodbye!

BytesIO:

1
2
3
4
5
6
>>> from io import BytesIO
>>> f = BytesIO()
>>> f.write('中文'.encode('utf-8'))
6
>>> print(f.getvalue())
b'\xe4\xb8\xad\xe6\x96\x87'

1
2
3
4
>>> from io import BytesIO
>>> f = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
>>> f.read()
b'\xe4\xb8\xad\xe6\x96\x87'

操作文件和目录

  • os.name 获取操作系统类型,os.uname() 获取系统详细信息(uname 在 windows 下不支持)
    posix: Linux、Unix 或 Mac OS X
    nt: Windows

  • os.environ 操作系统中的环境变量
    os.environ.get('path') 获取 path 变量的值

  • 操作文件和目录的函数一部分在 os,一部分在 os.path
    os.rname('text.txt', 'text.py') 文件重命名
    os.remove('test.py') 删除文件
    os.path.abspath('.') 绝对路径
    os.mkdir('/Users/michael/testdir') 创建目录
    os.rmdir('/Users/michael/testdir') 删除目录
    合成路径使用 os.path.join() 能正确处理不同操作系统的分隔符
    shutil 模块的 copyfile() 函数复制文件
    os.path.listdir('.') 列出该文件夹下的所有子一级文件夹和文件

实现 dir -l 功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import os,time
# 时间戳格式转换
def timenum2str(timenum):
# print('timenum = ', timenum)
t = time.localtime(timenum)
# print('t = ', t)
return time.strftime('%Y-%m-%d %H:%M', t)
def getSize(filepath):
size = str(os.path.getsize(filepath))
return size
def getTime(filepath):
time = os.path.getmtime(filepath)
return timenum2str(time)
if __name__ == '__main__':
for dir_name in [x for x in os.listdir('.')]:
print(getSize(dir_name), '\t', getTime(dir_name), '\t', dir_name)

序列化

  • pickle 模块实现序列化
    1.pick.dumps() 将对象序列化成 bytes 对象,或将对象序列化成 bytes 后写入文件
    1
    2
    3
    d = dict(name='Bob', age=20, score=88)
    with open('dump.txt', 'wb') as f:
    pickle.load(d, f)

2.pick.load() 从 bytes 中反序列化出对象,或者直接从 file-like obgect 中反序列

1
2
3
with open('dump.txt', 'rb') as f:
d = pickle.load(f)
print(d)

  • JSON 和 python 内置对象的转换
    dumps() 将 python 对象变成 JSON ,dump() 直接将 JSON 写入 file-like object
    loads() 将 JSON 字符串反序列化,load()file-like object 中读取并序列化

进程和线程

对于操作系统来说,一个任务就是一个进程(Process)

进程内的‘子任务’称为线程(Thread),线程是最小的执行单元,每个进程至少由一个线程组成。

多进程

nop

  • Unix/Linux 下,fork() 调用多进程
  • windows 使用跨平台 multiprocessing 模块实现多进程
  • Queue,Pipes 实现进程间通信

正则表达式

  • \d 一个数字
    \w 一个数字或者字母
    . 任意字符
    \s 一个空格(包含 Tab 等空白符)

  • * 任意个数
    + 至少一个
    ? 0个或者一个
    {n} n 个字符
    {n,m} n-m 个字符

  • ^ 开头,$ 结尾
    A|B 匹配 A 或 B
    [] 表示范围;[0-9a-zA-Z\_]
    特殊字符需用 \ 转义,比如下划线 _

  • re 模块
    直接用 match() 方法

    1
    re.match(r'^\d{3}\-\d{3,8}$', '010-12345')

预编译之后再用 match()

1
2
3
4
5
6
>>> re_telephone = re.compile(r'^(\d{3})-(\d{3,8})$')
# 使用:
>>> re_telephone.match('010-12345').groups()
('010', '12345')
>>> re_telephone.match('010-8086').groups()
('010', '8086')

() 包裹的为要返回的分组(Group),group()返回某一个,或 groups() 返回全部
group(0) 匹配的原始字符串,group(1) 第一个 ()

常用内建模块

datetime

nop

collections

nop

base64

  • 用 64 个字符表示任意二进制数据的编码

  • 3 个字节一组,划分为 4 个 6 bit,查表替换

  • 少于 3 字节的用 \00 补足,编码末尾加上一个或者两个 = ,表示补了多少字节

  • 编码后的长度一定是 4 的倍数,补足用 = 补足

  • 编码 base64.b64encode(b'abc')

  • 解码 base64.b64decode(b'YWJjZA==')

struct

解决 bytes 和其他二进制数据类型的转换

halib

摘要算法:将任意长度的数据转换成固定长度的数据串

  • md5
    生成固定 128 bit,用 32 个十六进制数表示
    数据块很大,可以分块调用 update() ,结果一样
    hexdigest() 十六进制摘要
    使用加盐(salt)的方法加强保护
1
2
3
4
5
6
import hashlib
md5 = hashlib.md5()
md5.update('how to use md5 '.encode('utf-8'))
md5.update('in python hashlib?'.encode('utf-8'))
print(md5.hexdigest())
  • sha1
    生成固定 160 bit,用 40 个十六进制数表示
1
2
3
4
5
6
import hashlib
sha1 = hashlib.sha1()
sha1.update('how to use sha1 in '.encode('utf-8'))
sha1.update('python hashlib?'.encode('utf-8'))
print(sha1.hexdigest())
  • sha256,sha512

hmac(Keyed-Hashing for Message Authentication)

hash 加盐

itertools

操作迭代函数

图形界面

支持的图形界面的第三方库
TK,wxWidgets,Qt,GTK

#网络编程

TCP/IP 简介

  • IP 协议
    标识互联网上计算机
    按块发送,不保证到达,也不保证按顺序到达
    IPv4,32 位整数:192.168.0.1
    IPV6,128 位整数:2001:0db8:85a3:0042:1000:8a2e:0370:7334(8 个 4 位 16进制,: 隔开)

  • TCP
    建立在 IP 之上
    可靠连接,保证顺序到达
    HTTP 协议, SMTP 协议建立在 TCP 之上

  • 端口
    用于进程间通信
    小于 1024 的是 internet 标准服务的端口
    常见端口号:
    80 Web 服务
    21 FTP
    25 SMTP

TCP编程

socket 通常表示打开了一个网络连接,打开一个 socket 需要知道目标地址的 ip,端口号,再指定协议类型

客户端

  • 导入 socket 库
    import socket

  • 创建基于 TCP 的 socket
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    AF_INET IPV4, AF_INET6 IPV6
    SOCK_STREAM 使用面向流的协议 TCP

  • 连接
    s.connect(('www.sina.com', 80))
    使用 tuple(元组),指明域名和端口号

  • 发送数据
    s.send(b'GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')

  • recv(max),接收数据,max 指明最大接收字节

服务端

  • 绑定端口
    s.bind('127.0.0.1', 9999)

  • 监听,指定等待连接的最大数量
    s.listen(5)

server.py

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
import socket
import threading
import time
def tcplink(sock, addr):
print('Accept new connection from %s:%s...' % 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)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('127.0.0.1', 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()

client.py

1
2
3
4
5
6
7
8
9
10
11
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 9999))
print(s.recv(1024).decode('utf-8'))
for data in [b'Michael', b'Tracy', b'Sarah']:
# 发送数据:
s.send(data)
print(s.recv(1024).decode('utf-8'))
s.send(b'exit')
s.close()

UDP 编程

  • 面向无连接的协议,不需要建立连接,只需要知道 IP 和端口号就可直接发送数据包

电子邮件

MUA(Mail User Agent) —— 邮件用户代理
MTA(Mail Transfer Agent) —— 邮件传输代理
MDA(Mail Delivery Agent) —— 邮件投递代理

电子邮件流程

1
发件人 -> MUA -> MTA -> 若干MTA -> MDA <- MUA <- 收件人

发邮件,MUA 和 MTA 使用 SMTP(Simple Mail Transfer Protocol)

收邮件,MDA 和 MUA 使用 POP:Post Office Protocol,目前版本是3,俗称POP3;IMAP:Internet Message Access Protocol,目前版本是4 两种协议

SMTP 发送邮件

模块 email 构造邮件,smtp 发送邮件

POP3收取邮件

nop

Web开发

nop

异步 IO

nop