python自学
python自学
缩进
转义符,换行
输入
1 | print('\\\t\\') |
变量赋值 str指针,不可变对象
1 | 'abc' a = |
list,tuple, set
1 | len(l) |
dictionary
1 | d = {'Michael': 95, 'Bob': 75, 'Tracy': 85} |
逻辑
1 | if <条件判断1>: |
函数
1 | def a(x,n): |
一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
1 | def add(x, y, f): |
特殊功能
切片,迭代……都是不分数据类型包括字符串的
列表生成式:
写列表生成式时,把要生成的元素x * x放到前面,后面跟for循环,就可以把list创建出来
1 | for x in range(1, 11) if x % 2 == 0] [x * x |
if放在前面必须有else
这是因为for
前面的部分是一个表达式,它必须根据x
计算出一个结果。因此,考察表达式:x if x % 2 == 0
,它无法根据x
计算出结果,因为缺少else
,必须加上else
:
1 | if x % 2 == 0 else -x for x in range(1, 11)] [x |
运用列表生成式,可以写出非常简洁的代码。例如,列出当前目录下的所有文件和目录名,可以通过一行代码实现:
1 | >>> import os # 导入os模块,模块的概念后面讲到 |
列表生成式也可以使用两个变量来生成list:
1 | >>> d = {'x': 'A', 'y': 'B', 'z': 'C' } |
最后把一个list中所有的字符串变成小写:
1 | >>> L = ['Hello', 'World', 'IBM', 'Apple'] |
生成器
如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
通过迭代器或生成器,可以创建无限长的序列,并按需逐步生成它们。
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]
改成()
,就创建了一个generator:
next()
函数获得generator的下一个返回值:
1 | g = (x * x for x in range(10)) |
fib
函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。
也就是说,上面的函数和generator仅一步之遥。要把fib
函数变成generator函数,只需要把print(b)
改为yield b
就可以了:
1 | def fib(max): |
这就是定义generator的另一种方法。如果一个函数定义中包含yield
关键字,那么这个函数就不再是一个普通函数,而是一个generator函数,调用一个generator函数将返回一个generator
普通函数是顺序执行,遇到return
语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行。
调用该generator函数时,首先要生成一个generator对象,然后用next()
函数不断获得下一个返回值:
1 | def odd(): |
iterator,iterable
惰性序列
Map/Reduce
map是一个高阶函数,map()
函数接收两个参数,一个是函数,一个是Iterable
,map
将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator
返回。
1 | list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])) |
再看reduce
的用法。reduce
把一个函数作用在一个序列[x1, x2, x3, ...]
上,这个函数必须接收两个参数,reduce
把结果继续和序列的下一个元素做累积计算,其效果就是:
1 | reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4) |
filter
和map()
类似,filter()
也接收一个函数和一个序列。和map()
不同的是,filter()
把传入的函数依次作用于每个元素,然后根据返回值是True
还是False
决定保留还是丢弃该元素。
例如,在一个list中,删掉偶数,只保留奇数,可以这么写:
1 | def is_odd(n): |
//////sort
模块
.py
放在文件夹里就是包
pip
模块搜索路径:
1.
当在 Python 中使用 import mymodule
试图加载一个模块时,Python 会在指定的路径下去查找对应的 .py
文件。如果没有找到该模块,就会抛出 ImportError
错误。
默认情况下,Python 会按照如下顺序来查找模块:
- 当前工作目录:这是你运行 Python 解释器的目录。
- 内置模块:Python 自带的模块,如
os
、sys
等。 - 第三方模块:通过包管理工具(如
pip
)安装的模块,通常位于site-packages
文件夹中。
2.在运行时动态添加路径
你可以通过在 Python 程序运行时修改 sys.path
来添加自定义路径。例如,你可以使用 sys.path.append()
来添加一个新的目录。
1 | import sys |
仅在当前运行有效
特殊变量:
面向对象
类,私有变量,初始化
高级
///////
错误调试和测试
assert
1 | assert n != 0, 'n is zero!' |
logging!
使用 logging
模块非常简单,以下是一个基本的使用示例:
1 | import logging |
输出结果:
1 | 2024-10-09 12:00:00,000 - DEBUG - 这是调试信息 |
format
控制了日志的输出格式。%(asctime)s
表示时间戳,%(levelname)s
表示日志级别,%(message)s
表示日志内容。level
参数可以控制日志的输出级别。level=logging.DEBUG
表示输出所有级别的日志,level=logging.ERROR
表示只输出错误及以上的日志信息。
4. 将日志写入文件
如果你希望将日志写入到文件中,可以在 basicConfig
中指定 filename
参数:
1 | import logging |
这会在当前目录下生成一个名为 app.log
的文件,所有日志内容都会写入到该文件中。
5. 设置不同的日志处理器
你可以设置多个日志处理器(Handler),例如将日志同时输出到控制台和文件中。
1 | import logging |
6. 日志中的变量
你还可以将变量值包含在日志信息中:
1 | python复制代码name = 'Alice' |
这条信息会输出:
1 | 2024-10-09 12:00:00,003 - INFO - 用户 Alice 已登录 |
7. 调试复杂应用
在复杂应用中使用 logging
模块可以帮助你更好地调试。例如,你可以在函数的入口和出口处打印调试信息,以跟踪函数的调用情况和执行流程。
1 | import logging |
输出:
1 | 2024-10-09 12:00:00,004 - DEBUG - 开始 divide 函数: a=10, b=2 |
编写单元测试,文档测试
io
同步io,异步io
文件读写
os进程线程
fork()
p=Process(target=run_proc, args=(‘test’,))
p.start()
p.join()
pool
tcp/ip
互联网上每个计算机的唯一标识就是IP地址,类似123.123.123.123
。如果一台计算机同时接入到两个或更多的网络,比如路由器,它就会有两个或多个IP地址,所以,IP地址对应的实际上是计算机的网络接口,通常是网卡。
IP协议负责把数据从一台计算机通过网络发送到另一台计算机。数据被分割成一小块一小块,然后通过IP包发送出去。由于互联网链路复杂,两台计算机之间经常有多条线路,因此,路由器就负责决定如何把一个IP包转发出去。IP包的特点是按块发送,途径多个路由,但不保证能到达,也不保证顺序到达。
TCP协议负责在两台计算机之间建立可靠连接,保证数据包按顺序到达。TCP协议会通过握手建立连接,然后,对每个IP包编号,确保对方按顺序收到,如果包丢掉了,就自动重发。
一个TCP报文除了包含要传输的数据外,还包含源IP地址和目标IP地址,源端口和目标端口。
端口有什么作用?在两台计算机通信时,只发IP地址是不够的,因为同一台计算机上跑着多个网络程序。一个TCP报文来了之后,到底是交给浏览器还是QQ,就需要端口号来区分。每个网络程序都向操作系统申请唯一的端口号,这样,两个进程在两台计算机之间建立网络连接就需要各自的IP地址和各自的端口号。
一个进程也可能同时与多个计算机建立链接,因此它会申请很多端口。
socket
tcp
客户端
一个Socket表示“打开了一个网络链接”
1 | import socket |
服务器端
1 | socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
然后,我们要绑定监听的地址和端口。服务器可能有多块网卡,可以绑定到某一块网卡的IP地址上,也可以用0.0.0.0
绑定到所有的网络地址,还可以用127.0.0.1
绑定到本机地址。127.0.0.1
是一个特殊的IP地址,表示本机地址,如果绑定到这个地址,客户端必须同时在本机运行才能连接
1 | s.bind(('127.0.0.1', 9999)) |
1 | def tcplink(sock, addr): |
udp
使用UDP协议时,不需要建立连接,只需要知道对方的IP地址和端口号,就可以直接发数据包。但是,能不能到达就不知道了。