Compare commits
15 Commits
4d74d5ea44
...
main
Author | SHA1 | Date | |
---|---|---|---|
bcfc9e15b5 | |||
4f9f49a2e0 | |||
7b53b37b3b | |||
836df0d56c | |||
df099c7f1a | |||
ef0ed27b9e | |||
66148187d0 | |||
244627cf69 | |||
3ed89009e5 | |||
4222354358 | |||
ed1ace678b | |||
d5f0bd6570 | |||
47e3b9a068 | |||
355a5a52f3 | |||
36ca88991e |
@@ -65,12 +65,12 @@ import time
|
||||
def timer(func):
|
||||
def inner(*args,**kwargs):
|
||||
start = time.time()
|
||||
func(args,kwargs)
|
||||
func(*args,**kwargs)
|
||||
print(time.time() - start)
|
||||
return inner
|
||||
@timer
|
||||
def func1(*args,**kwargs):
|
||||
print(args,kwargs)
|
||||
print(*args,**kwargs)
|
||||
func1('hello world','abc',123,432)
|
||||
```
|
||||
|
||||
|
@@ -14,7 +14,7 @@ variable = [out_exp_res for out_exp in input_list if out_exp == 2]
|
||||
30以内所有能被3整除的数
|
||||
|
||||
```python
|
||||
multiples = [i for i in range(30) if i % 3 is 0]
|
||||
multiples = [i for i in range(30) if i % 3 == 0]
|
||||
print(multiples)
|
||||
```
|
||||
|
||||
@@ -23,7 +23,7 @@ print(multiples)
|
||||
```python
|
||||
def squared(x):
|
||||
return x*x
|
||||
multiples = [squared(i) for i in range(30) if i % 3 is 0]
|
||||
multiples = [squared(i) for i in range(30) if i % 3 == 0]
|
||||
print(multiples)
|
||||
```
|
||||
|
||||
|
@@ -79,7 +79,7 @@ for i in range(n):
|
||||
说明:这段代码,for循环 里面的代码会执行n遍,因此它消耗的时间是随着n的变化而变
|
||||
化的,因此这类代码都可以用O(n)来表示它的时间复杂度
|
||||
|
||||
## 线性对数阶 O(nlogN)
|
||||
## 线性对数阶 O($$nlogN$$)
|
||||
|
||||
```python
|
||||
n = 100
|
||||
@@ -102,7 +102,7 @@ for i in range(n):
|
||||
j += i
|
||||
```
|
||||
说明:平方阶$$O(n2)$$就更容易理解了,如果把$$O(n)$$的代码再嵌套循环一遍,它的时间复杂度就是$$O(n^2)$$,这段代码其实就是嵌套了2层n循环,它的时间复杂度就是$$O(n*n)$$,即$$O(n^2)$$ 如果将其中一层循环的n改成m,那它的时间复杂度就变成了$$O(m*n)$$
|
||||
## 立方阶 O(n³)、K 次方阶 O(n^k)
|
||||
## 立方阶 O(n³)、K 次方阶 O($$n^k$$)
|
||||
|
||||
说明:参考上面的 O(n²) 去理解就好了,O(n³)相当于三层 n 循环,其它的类似
|
||||
|
||||
|
@@ -2,7 +2,13 @@
|
||||
|
||||
## 反射
|
||||
|
||||
python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
|
||||
- python面向对象中的反射:
|
||||
- 指程序在**运行时**动态地获取对象(包括类、模块、实例等)的结构信息(如属性、方法、类型),并能动态地操作这些结构(如调用方法、修改属性、创建对象)的能力。简单来说,反射让程序能在运行时“自省”——了解自身的组成,并根据需要灵活调整行为。
|
||||
- 反射的常见应用场景
|
||||
- **动态配置加载**:根据配置文件(如JSON、YAML)动态设置对象属性。例如,通过`setattr`将配置中的`debug=True`映射到对象的`debug`属性。
|
||||
- **插件化架构**:通过`importlib.import_module`动态加载插件模块,再用`getattr`获取插件类的实例并调用其方法。例如,电商系统可动态加载“限时抢购”“团购”等插件订单类。
|
||||
- **通用工具函数**:编写适用于不同对象的通用工具。例如,`print_object_info`函数通过`dir`获取对象所有属性,再用`getattr`打印属性值。
|
||||
- **ORM框架**:通过反射将数据库表的列映射到Python类的属性。例如,Django ORM通过反射获取模型的字段信息,动态生成SQL语句。
|
||||
|
||||
四个可以实现自省的函数
|
||||
|
||||
@@ -35,15 +41,16 @@ func()
|
||||
print(getattr(obj,'aaaaaaaa','不存在啊')) # 报错
|
||||
|
||||
# 设置属性
|
||||
setattr(obj,'sb',True)
|
||||
setattr(obj,'show_name',lambda self:self.name+'sb')
|
||||
setattr(obj,'food','面条')
|
||||
print(getattr(obj, 'food'))
|
||||
setattr(obj, 'eat', lambda self: self.name + "在吃" + self.food)
|
||||
print(obj.__dict__)
|
||||
print(obj.show_name(obj))
|
||||
print(obj.eat(obj))
|
||||
|
||||
# 删除属性
|
||||
delattr(obj,'age')
|
||||
delattr(obj,'show_name')
|
||||
# delattr(obj,'show_name111') # 不存在,则报错
|
||||
delattr(obj,'eat')
|
||||
# delattr(obj,'eat111') # 不存在,则报错
|
||||
|
||||
print(obj.__dict__)
|
||||
```
|
||||
@@ -161,6 +168,71 @@ while 1:
|
||||
print('输入错误。。。。')
|
||||
```
|
||||
|
||||
## 元编程特性
|
||||
|
||||
**元编程(Metaprogramming)**是Python中一种高级编程范式,其核心思想是**编写能够操作代码本身的程序**——通过在运行时动态生成、修改或执行代码,实现对程序行为的灵活控制。这种特性让Python具备极强的动态性与抽象能力,成为框架开发、代码生成等场景的基石。
|
||||
|
||||
### **1. 装饰器**
|
||||
|
||||
**函数/类的行为扩展工具**
|
||||
|
||||
装饰器是Python最常用的元编程技术之一,本质是**接收函数或类作为输入、返回修改后函数/类的高阶函数**。通过`@decorator`语法糖,可在不修改原代码的情况下,为函数/类添加日志、性能监控、权限验证等功能。
|
||||
|
||||
- **基础装饰器**:例如日志装饰器`@log_decorator`,可在函数调用前后打印日志;
|
||||
- **带参数的装饰器**:通过外层函数接收参数,实现更灵活的功能控制(如`@repeat(times=3)`让函数执行3次);
|
||||
- **类装饰器**:用于修改类的行为,例如实现单例模式(确保类只有一个实例)、自动注册类到全局字典(如Django模型注册)。
|
||||
|
||||
### **2. 元类**
|
||||
|
||||
**类的创建控制器**
|
||||
|
||||
元类是**创建类的类**(默认元类为`type`),通过继承`type`并重写`__new__`或`__init__`方法,可控制类的创建过程,实现动态修改类属性、添加方法、强制类遵循接口规范等功能。
|
||||
|
||||
- **动态添加属性/方法**:例如在类创建时自动添加`author`属性或`hello`方法;
|
||||
- **强制接口检查**:通过元类确保类实现了特定方法(如`run`方法),避免类定义不完整;
|
||||
- **动态注册类**:例如ORM框架中,通过元类自动将模型类注册到全局注册表,方便后续生成SQL语句。
|
||||
|
||||
### **3. 动态属性与描述符**
|
||||
|
||||
**属性访问的控制者**
|
||||
|
||||
动态属性允许在运行时修改对象的属性,而描述符则通过定义`__get__`、`__set__`等方法,精确控制属性的访问行为。
|
||||
|
||||
- **动态属性**:通过`__getattr__`(获取不存在的属性)、`__setattr__`(设置属性)等方法,实现动态属性访问(如代理类通过`__getattr__`转发属性访问);
|
||||
- **描述符**:用于实现数据验证、只读属性等功能。例如`ReadOnlyDescriptor`可防止属性被修改,`ValidatedDescriptor`可验证属性值的合法性(如确保年龄为正数)。
|
||||
|
||||
### **4. 反射**
|
||||
|
||||
**程序的自省能力**
|
||||
|
||||
反射是程序**在运行时检查、修改自身结构**的能力,通过内置函数(`getattr`、`hasattr`、`setattr`、`delattr`)和`inspect`模块,可动态获取对象的属性、方法信息,或修改对象行为。
|
||||
|
||||
- **获取信息**:`hasattr(obj, 'name')`检查对象是否有`name`属性,`getattr(obj, 'name')`获取`name`属性的值;
|
||||
- **修改属性**:`setattr(obj, 'age', 30)`设置对象的`age`属性为30,`delattr(obj, 'age')`删除`age`属性;
|
||||
- **动态调用方法**:`getattr(obj, 'method')()`获取并调用对象的`method`方法。
|
||||
|
||||
### **5. 动态代码生成与执行**
|
||||
|
||||
**运行时代码的灵活构建**
|
||||
|
||||
通过`exec`、`compile`、`eval`等函数,可在运行时生成并执行代码字符串,实现动态代码生成。
|
||||
|
||||
- **动态创建函数**:例如`exec`生成`add`函数(`def add(a, b): return a + b`),并通过`eval`获取函数对象;
|
||||
- **动态修改方法**:猴子补丁(Monkey Patching)通过动态修改现有类的方法,扩展其功能(如为`Logger`类动态添加`log_warning`方法);
|
||||
- **动态生成类**:通过`type`函数动态创建类(如`type('DynamicClass', (), {'attr': 42})`创建包含`attr`属性的类)。
|
||||
|
||||
### **6. AST操作**
|
||||
|
||||
**代码结构的深度修改**
|
||||
|
||||
`ast`(抽象语法树)模块允许解析、修改Python代码的语法结构,实现更高级的代码生成与修改。
|
||||
|
||||
- **解析代码**:将代码字符串解析为AST(如`ast.parse("a = 1 + 2")`);
|
||||
- **修改AST**:遍历AST节点,修改代码结构(如将`a = 1 + 2`修改为`a = 1 * 2`);
|
||||
- **生成代码**:将修改后的AST编译为可执行代码(如`compile`函数)。
|
||||
|
||||
元编程的灵活性使其成为Python框架(如Django、Flask、SQLAlchemy)的核心技术,但也需注意**代码可读性**(过度使用元编程会增加代码复杂度)、**调试难度**(动态生成的代码难以跟踪)、**安全性**(动态执行代码可能引发安全漏洞)等问题。合理使用元编程,能让代码更简洁、灵活,提升开发效率。
|
||||
|
||||
## 函数 vs 方法
|
||||
|
||||
### 通过打印函数(方法)名确定
|
||||
@@ -218,7 +290,7 @@ class A:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def func2(self):
|
||||
def func2():
|
||||
pass
|
||||
|
||||
|
||||
@@ -241,11 +313,11 @@ print(isinstance(obj.func2,FunctionType))
|
||||
|
||||
4. 方法可以操作类内部的数据。
|
||||
|
||||
5. 方法跟对象是关联的。如我们在用strip()方法是,是不是都是要通过str对象调用,比如我们有字符串s,然后s.strip()这样调用。是的,strip()方法属于str对象。
|
||||
5. 方法跟对象是关联的。如我们在用strip()方法时,是不是都是要通过str对象调用,比如我们有字符串s,然后s.strip()这样调用,strip()方法属于str对象。
|
||||
|
||||
我们或许在日常中会口语化称呼函数和方法时不严谨,但是我们心中要知道二者之间的区别。
|
||||
|
||||
在其他语言中,如Java中只有方法,C中只有函数,C++么,则取决于是否在类中
|
||||
在其他语言中,如Java中只有方法,C中只有函数,C++则取决于是否在类中
|
||||
|
||||
## 双下方法
|
||||
|
||||
@@ -409,14 +481,14 @@ class A:
|
||||
|
||||
```python
|
||||
class Foo:
|
||||
def __init__(self,name):
|
||||
self.name=name
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __getitem__(self, item):
|
||||
print(self.__dict__[item])
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.__dict__[key]=value
|
||||
self.__dict__[key] = value
|
||||
print('赋值成功')
|
||||
|
||||
def __delitem__(self, key):
|
||||
@@ -427,12 +499,13 @@ class Foo:
|
||||
print('del obj.key时,我执行')
|
||||
self.__dict__.pop(item)
|
||||
|
||||
f1=Foo('sb')
|
||||
f1['age']=18
|
||||
f1['age1']=19
|
||||
|
||||
f1 = Foo('张三')
|
||||
f1['age'] = 18
|
||||
f1['age1'] = 19
|
||||
del f1.age1
|
||||
del f1['age']
|
||||
f1['name']='mingzi'
|
||||
f1['name'] = '李四'
|
||||
print(f1.__dict__)
|
||||
```
|
||||
|
||||
@@ -477,8 +550,9 @@ class Diycontextor:
|
||||
self.filehander.close()
|
||||
|
||||
|
||||
with Diycontextor('config', 'r') as f:
|
||||
for i in f:
|
||||
with Diycontextor('config', 'a+') as f:
|
||||
f.seek(0)
|
||||
for i in f.readlines():
|
||||
print(i.strip())
|
||||
```
|
||||
|
||||
@@ -539,16 +613,16 @@ class RoleConfig(StarkConfig):
|
||||
class AdminSite:
|
||||
|
||||
def __init__(self):
|
||||
self._registry = {}
|
||||
self.registry_db = {}
|
||||
|
||||
def register(self, k, v):
|
||||
self._registry[k] = v
|
||||
self.registry_db[k] = v
|
||||
|
||||
|
||||
site = AdminSite()
|
||||
site.register(UserInfo, StarkConfig)
|
||||
# 1
|
||||
obj = site._registry[UserInfo]()
|
||||
obj = site.registry_db[UserInfo](100)
|
||||
|
||||
# 2
|
||||
# obj = site._registry[UserInfo](100)
|
||||
@@ -585,11 +659,15 @@ class AdminSite:
|
||||
def register(self,k,v):
|
||||
self._registry[k] = v(k)
|
||||
|
||||
@property
|
||||
def registry_db(self):
|
||||
return self._registry
|
||||
|
||||
site = AdminSite()
|
||||
site.register(UserInfo,StarkConfig)
|
||||
site.register(Department,RoleConfig)
|
||||
|
||||
for k,row in site._registry.items():
|
||||
for k,row in site.registry_db.items():
|
||||
row.run()
|
||||
```
|
||||
|
||||
|
BIN
02.面向对象/代码生成式游戏/KKRIEGER.EXE
Normal file
BIN
02.面向对象/代码生成式游戏/KKRIEGER.EXE
Normal file
Binary file not shown.
@@ -215,6 +215,7 @@ import socket
|
||||
|
||||
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
||||
phone.bind(('127.0.0.1',8080))
|
||||
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
phone.listen(5)
|
||||
|
||||
while 1:
|
||||
@@ -222,16 +223,16 @@ while 1:
|
||||
print(conn,client_addr,sep='\n')
|
||||
|
||||
while 1:
|
||||
try:
|
||||
if conn:
|
||||
from_client_data = conn.recv(1024)
|
||||
if len(from_client_data) == 0 or from_client_data.decode('utf-8') == 'q':
|
||||
# 如果客户端返回为空或者q,说明已经断开
|
||||
break
|
||||
print(from_client_data.decode('utf-8'))
|
||||
|
||||
conn.send(from_client_data.upper())
|
||||
except:
|
||||
else:
|
||||
break
|
||||
|
||||
conn.close()
|
||||
phone.close()
|
||||
```
|
||||
|
||||
客户端
|
||||
@@ -244,9 +245,11 @@ phone.connect(('127.0.0.1',8080))
|
||||
|
||||
while 1:
|
||||
client_data = input('>>> ')
|
||||
if len(client_data.strip()) == 0:
|
||||
continue
|
||||
phone.send(client_data.encode('utf-8'))
|
||||
if client_data == 'q':break
|
||||
|
||||
if client_data == 'q':
|
||||
break
|
||||
from_server_data = phone.recv(1024)
|
||||
print(from_server_data.decode('utf-8'))
|
||||
|
||||
@@ -550,6 +553,67 @@ phone.close()
|
||||
|
||||
## 粘包的解决方案
|
||||
|
||||
### 添加结束字符
|
||||
|
||||
在每次发送一个信息结束的地方,添加一个标识,在从缓冲区获取数据时候,根据表示来判断消息是否已经获取结束
|
||||
|
||||
- 服务端
|
||||
|
||||
```python
|
||||
import socket
|
||||
import subprocess
|
||||
|
||||
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
||||
phone.bind(('127.0.0.1', 8080))
|
||||
|
||||
phone.listen(5)
|
||||
|
||||
while 1: # 循环连接客户端
|
||||
conn, client_addr = phone.accept()
|
||||
print(client_addr)
|
||||
|
||||
while 1:
|
||||
try:
|
||||
cmd = conn.recv(1024)
|
||||
ret = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
correct_msg = ret.stdout.read()
|
||||
error_msg = ret.stderr.read()
|
||||
conn.send(correct_msg + error_msg + "結束".encode("gbk"))
|
||||
except ConnectionResetError:
|
||||
break
|
||||
|
||||
conn.close()
|
||||
phone.close()
|
||||
```
|
||||
|
||||
- 客户端
|
||||
|
||||
```python
|
||||
import socket
|
||||
|
||||
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 买电话
|
||||
|
||||
phone.connect(('127.0.0.1',8080)) # 与客户端建立连接, 拨号
|
||||
|
||||
|
||||
while 1:
|
||||
cmd = input('>>>')
|
||||
phone.send(cmd.encode('utf-8'))
|
||||
|
||||
from_server_data = b""
|
||||
while True:
|
||||
from_server_data += phone.recv(1024)
|
||||
if from_server_data.decode('gbk').count("結束") > 0:
|
||||
break
|
||||
|
||||
print(from_server_data.decode('gbk'))
|
||||
|
||||
phone.close()
|
||||
```
|
||||
|
||||
- 这样只是解决了一次消息没有收完整的情况,但是一次接受1024,可能会将紧跟的下一条消息的开头也一并接收了
|
||||
|
||||
### struct模块
|
||||
|
||||
该模块可以把一个类型,如数字,转成固定长度的bytes
|
||||
@@ -567,6 +631,8 @@ ret1 = struct.unpack('i',ret)[0]
|
||||
print(ret1, type(ret1), len(ret1))
|
||||
|
||||
# 但是通过struct 处理不能处理太大
|
||||
# 可处理的范围是 -2,147,483,648 ~ 2,147,483,647(即 -2³¹ ~ 2³¹-1)。
|
||||
# 如果超出范围,就会发生异常,struct.error
|
||||
```
|
||||
|
||||
方案一:
|
||||
@@ -640,7 +706,12 @@ while 1:
|
||||
res = b''
|
||||
|
||||
while recv_size < total_size:
|
||||
recv_data = phone.recv(1024)
|
||||
# 精准的获取数据的长度,防止将后面跟着的数据头部获取到
|
||||
if (total_size - recv_size) > 1024:
|
||||
recv_buffer = 1024
|
||||
else:
|
||||
recv_buffer = total_size - recv_size
|
||||
recv_data = phone.recv(recv_buffer)
|
||||
res += recv_data
|
||||
recv_size += len(recv_data)
|
||||
|
||||
@@ -754,13 +825,19 @@ while 1:
|
||||
res = b''
|
||||
|
||||
while recv_size < total_size:
|
||||
recv_data = phone.recv(1024)
|
||||
# 精准的获取数据的长度,防止将后面跟着的数据头部获取到
|
||||
if (total_size - recv_size) > 1024:
|
||||
recv_buffer = 1024
|
||||
else:
|
||||
recv_buffer = total_size - recv_size
|
||||
recv_data = phone.recv(recv_buffer)
|
||||
res += recv_data
|
||||
recv_size += len(recv_data)
|
||||
|
||||
print(res.decode('gbk'))
|
||||
|
||||
phone.close()
|
||||
|
||||
```
|
||||
|
||||
FTP上传下载文件的代码(简单版)
|
||||
@@ -772,7 +849,6 @@ import socket
|
||||
import struct
|
||||
import json
|
||||
sk = socket.socket()
|
||||
## buffer = 4096 # 当双方的这个接收发送的大小比较大的时候,就像这个4096,就会丢数据,这个等我查一下再告诉大家,改小了就ok的,在linux上也是ok的。
|
||||
buffer = 1024 #每次接收数据的大小
|
||||
sk.bind(('127.0.0.1',8090))
|
||||
sk.listen()
|
||||
@@ -791,9 +867,10 @@ with open(head['filename'],'wb') as f:
|
||||
f.write(content)
|
||||
filesize -= buffer
|
||||
else:
|
||||
content = conn.recv(buffer)
|
||||
content = conn.recv(filesize)
|
||||
f.write(content)
|
||||
break
|
||||
print(head['filename'], "接收成功")
|
||||
|
||||
conn.close()
|
||||
sk.close()
|
||||
@@ -810,15 +887,14 @@ sk = socket.socket()
|
||||
sk.connect(('127.0.0.1',8090))
|
||||
buffer = 1024 #读取文件的时候,每次读取的大小
|
||||
head = {
|
||||
'filepath':r'C:\Users\Aaron\Desktop\新建文件夹', #需要下载的文件路径,也就是文件所在的文件夹
|
||||
'filename':'config', #改成上面filepath下的一个文件
|
||||
'filepath':r'C:\Users\simid\Desktop', #需要下载的文件路径,也就是文件所在的文件夹
|
||||
'filename':'KKRIEGER.EXE', #改成上面filepath下的一个文件
|
||||
'filesize':None,
|
||||
}
|
||||
|
||||
file_path = os.path.join(head['filepath'],head['filename'])
|
||||
filesize = os.path.getsize(file_path)
|
||||
head['filesize'] = filesize
|
||||
# json_head = json.dumps(head,ensure_ascii=False) #字典转换成字符串
|
||||
json_head = json.dumps(head) #字典转换成字符串
|
||||
bytes_head = json_head.encode('utf-8') #字符串转换成bytes类型
|
||||
print(json_head)
|
||||
|
@@ -8,6 +8,8 @@
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
程序员将对应于程序和数据的已穿孔的纸带(或卡片)装入输入机,然后启动输入机把程序和数据输入计算机内存,接着通过控制台开关启动程序针对数据运行;计算完毕,打印机输出计算结果;用户取走结果并卸下纸带(或卡片)后,才让下一个用户上机。
|
||||
|
||||
手工操作方式两个特点:
|
||||
@@ -87,8 +89,12 @@
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
分时技术:把处理机的运行时间分成很短的时间片,按时间片轮流把处理机分配给各联机作业使用。
|
||||
若某个作业在分配给它的时间片内不能完成其计算,则该作业暂时中断,把处理机让给另一作业使用,等待下一轮时再继续其运行。由于计算机速度很快,作业运行轮转得很快,给每个用户的印象是,好象他独占了一台计算机。而每个用户可以通过自己的终端向系统发出各种操作控制命令,在充分的人机交互情况下,完成作业的运行。
|
||||
具有上述特征的计算机系统称为分时系统,它允许多个用户同时联机使用计算机。
|
||||
|
@@ -71,7 +71,7 @@
|
||||
|
||||
### 状态介绍
|
||||
|
||||

|
||||

|
||||
|
||||
在了解其他概念之前,我们首先要了解进程的几个状态。在程序运行的过程中,由于被操作系统的调度算法控制,程序会进入几个状态:就绪,运行和阻塞。
|
||||
|
||||
@@ -172,8 +172,9 @@ process模块是一个创建进程的模块,借助这个模块,就可以完
|
||||
|
||||
方法介绍
|
||||
|
||||
| p.start() | 启动进程,并调用该子进程中的p.run() |
|
||||
| 方法 | 描述 |
|
||||
| ----------------- | ------------------------------------------------------------ |
|
||||
| p.start() | 启动进程,并调用该子进程中的p.run() |
|
||||
| p.run() | 进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法 |
|
||||
| p.terminate() | 强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁 |
|
||||
| p.is_alive() | 如果p仍然运行,返回True |
|
||||
@@ -181,8 +182,9 @@ process模块是一个创建进程的模块,借助这个模块,就可以完
|
||||
|
||||
属性介绍
|
||||
|
||||
| p.daemon | 默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置 |
|
||||
| 方法 | 描述 |
|
||||
| ---------- | ------------------------------------------------------------ |
|
||||
| p.daemon | 默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置 |
|
||||
| p.name | 进程的名称 |
|
||||
| p.pid | 进程的pid |
|
||||
| p.exitcode | 进程在运行时为None、如果为–N,表示被信号N结束(了解即可) |
|
||||
@@ -209,7 +211,7 @@ if __name__ == '__main__':
|
||||
print('主程序')
|
||||
```
|
||||
|
||||
使用join方法
|
||||
使用join方法,此方法会等待子进程结束
|
||||
|
||||
```python
|
||||
import time
|
||||
@@ -318,9 +320,9 @@ class MyProcess(Process):
|
||||
print('%s 正在和女主播聊天' %self.name)
|
||||
|
||||
if __name__ == '__main__':
|
||||
p1 = MyProcess('陈松')
|
||||
p2 = MyProcess('松哥')
|
||||
p3 = MyProcess('松松')
|
||||
p1 = MyProcess('张三')
|
||||
p2 = MyProcess('李四')
|
||||
p3 = MyProcess('王五')
|
||||
|
||||
p1.start()
|
||||
p2.start()
|
||||
@@ -375,7 +377,7 @@ class Myprocess(Process):
|
||||
print('%s正在和女主播聊天' %self.person)
|
||||
|
||||
if __name__ == '__main__':
|
||||
p=Myprocess('陈松')
|
||||
p=Myprocess('张三')
|
||||
p.daemon=True #一定要在p.start()前设置,设置p为守护进程,禁止p创建子进程,并且父进程代码执行结束,p即终止运行
|
||||
p.start()
|
||||
time.sleep(10) # 在sleep时查看进程id对应的进程
|
||||
@@ -467,12 +469,12 @@ class Myprocess(Process):
|
||||
super().__init__()
|
||||
|
||||
def run(self):
|
||||
print('%s正在和陈松聊天' %self.name)
|
||||
print('%s正在和张三聊天' %self.name)
|
||||
time.sleep(random.randrange(1,5))
|
||||
print('%s还在和陈松聊天' %self.name)
|
||||
print('%s还在和张三聊天' %self.name)
|
||||
|
||||
if __name__ == '__main__':
|
||||
p1=Myprocess('陈松')
|
||||
p1=Myprocess('李四')
|
||||
p1.start()
|
||||
|
||||
p1.terminate() # 关闭进程,不会立即关闭,所以is_alive立刻查看的结果可能还是存活
|
||||
@@ -501,7 +503,7 @@ class Myprocess(Process):
|
||||
# print('%s正在和网红脸聊天' %self.person)
|
||||
|
||||
if __name__ == '__main__':
|
||||
p1=Myprocess('陈松')
|
||||
p1=Myprocess('张三')
|
||||
p1.start()
|
||||
print(p1.pid) #可以查看子进程的进程id
|
||||
```
|
||||
@@ -581,7 +583,7 @@ if __name__ == '__main__':
|
||||
p.start()
|
||||
```
|
||||
|
||||
用说来保护票
|
||||
用锁来保护票
|
||||
|
||||
```python
|
||||
#文件db的内容为:{"count":1}
|
||||
|
BIN
03.网络编程与并发/03.多进程/image-20250918143648878.png
Normal file
BIN
03.网络编程与并发/03.多进程/image-20250918143648878.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 136 KiB |
BIN
03.网络编程与并发/03.多进程/image-20250918143803006.png
Normal file
BIN
03.网络编程与并发/03.多进程/image-20250918143803006.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 133 KiB |
@@ -31,7 +31,7 @@
|
||||
|
||||
### 使用线程的实际场景
|
||||
|
||||
开启一个字处理软件进程,该进程肯定需要办不止一件事情,比如监听键盘输入,处理文字,定时自动将文字保存到硬盘,这三个任务操作的都是同一块数据,因而不能用多进程。只能在一个进程里并发地开启三个线程,如果是单线程,那就只能是,键盘输入时,不能处理文字和自动保存,自动保存时又不能输入和处理文字。
|
||||
开启一个软件进程,该进程肯定需要办不止一件事情,比如监听键盘输入,处理文字,定时自动将文字保存到硬盘,这三个任务操作的都是同一块数据,因而不能用多进程。只能在一个进程里并发地开启三个线程,如果是单线程,那就只能是,键盘输入时,不能处理文字和自动保存,自动保存时又不能输入和处理文字。
|
||||
|
||||
### 内存中的线程
|
||||
|
||||
@@ -50,9 +50,9 @@
|
||||
### 全局解释器锁GIL
|
||||
|
||||
Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行。
|
||||
对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。
|
||||
对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。
|
||||
|
||||
在多线程环境中,Python 虚拟机按以下方式执行:
|
||||
在多线程环境中,Python 虚拟机按以下方式执行:
|
||||
|
||||
1. 设置 GIL;
|
||||
|
||||
@@ -65,7 +65,8 @@ Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Pyt
|
||||
5. 解锁 GIL;
|
||||
|
||||
6. 再次重复以上所有步骤。
|
||||
在调用外部代码(如 C/C++扩展函数)的时候,GIL将会被锁定,直到这个函数结束为止(由于在这期间没有Python的字节码被运行,所以不会做线程切换)编写扩展的程序员可以主动解锁GIL。
|
||||
|
||||
在调用外部代码(如 C/C++扩展函数)的时候,GIL将会被锁定,直到这个函数结束为止(由于在这期间没有Python的字节码被运行,所以不会做线程切换)编写扩展的程序可以主动解锁GIL。
|
||||
|
||||
### python线程模块的选择
|
||||
|
||||
@@ -87,7 +88,7 @@ def sayhi(name):
|
||||
print('%s say hello' %name)
|
||||
|
||||
if __name__ == '__main__':
|
||||
t=Thread(target=sayhi,args=('aaron',))
|
||||
t=Thread(target=sayhi,args=('张三',))
|
||||
t.start()
|
||||
print('主线程')
|
||||
```
|
||||
@@ -107,7 +108,7 @@ class Sayhi(Thread):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
t = Sayhi('aaron')
|
||||
t = Sayhi('张三')
|
||||
t.start()
|
||||
print('主线程')
|
||||
```
|
||||
@@ -256,7 +257,7 @@ import os
|
||||
def work():
|
||||
import time
|
||||
time.sleep(3)
|
||||
print(threading.current_thread().getName())
|
||||
print(threading.current_thread().name)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
@@ -264,7 +265,7 @@ if __name__ == '__main__':
|
||||
t=Thread(target=work)
|
||||
t.start()
|
||||
|
||||
print(threading.current_thread().getName())
|
||||
print(threading.current_thread().name)
|
||||
print(threading.current_thread()) #主线程
|
||||
print(threading.enumerate()) #连同主线程在内有两个运行的线程
|
||||
print(threading.active_count())
|
||||
@@ -281,7 +282,7 @@ def sayhi(name):
|
||||
print('%s say hello' %name)
|
||||
|
||||
if __name__ == '__main__':
|
||||
t=Thread(target=sayhi,args=('aaron',))
|
||||
t=Thread(target=sayhi,args=('张三',))
|
||||
t.start()
|
||||
t.join()
|
||||
print('主线程')
|
||||
@@ -305,8 +306,8 @@ def sayhi(name):
|
||||
print('%s say hello' %name)
|
||||
|
||||
if __name__ == '__main__':
|
||||
t=Thread(target=sayhi,args=('aaron',))
|
||||
t.setDaemon(True) #必须在t.start()之前设置
|
||||
t=Thread(target=sayhi,args=('张三',))
|
||||
t.daemon = True #必须在t.start()之前设置
|
||||
t.start()
|
||||
|
||||
print('主线程')
|
||||
@@ -433,8 +434,7 @@ from threading import current_thread,Thread,Lock
|
||||
import os,time
|
||||
def task():
|
||||
#未加锁的代码并发运行
|
||||
time.sleep(3)
|
||||
print('%s start to run' %current_thread().getName())
|
||||
print('%s start to run' %current_thread().name)
|
||||
global n
|
||||
#加锁的代码串行运行
|
||||
lock.acquire()
|
||||
@@ -459,34 +459,10 @@ if __name__ == '__main__':
|
||||
```
|
||||
|
||||
有的同学可能有疑问:既然加锁会让运行变成串行,那么我在start之后立即使用join,就不用加锁了啊,也是串行的效果啊
|
||||
没错:在start之后立刻使用jion,肯定会将100个任务的执行变成串行,毫无疑问,最终n的结果也肯定是0,是安全的,但问题是
|
||||
没错:在start之后立刻使用join,肯定会将100个任务的执行变成串行,毫无疑问,最终n的结果也肯定是0,是安全的,但问题是
|
||||
start后立即join:任务内的所有代码都是串行执行的,而加锁,只是加锁的部分即修改共享数据的部分是串行的
|
||||
单从保证数据安全方面,二者都可以实现,但很明显是加锁的效率更高.
|
||||
|
||||
```python
|
||||
from threading import current_thread,Thread,Lock
|
||||
import os,time
|
||||
def task():
|
||||
time.sleep(3)
|
||||
print('%s start to run' %current_thread().getName())
|
||||
global n
|
||||
temp=n
|
||||
time.sleep(0.5)
|
||||
n=temp-1
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
n=100
|
||||
lock=Lock()
|
||||
start_time=time.time()
|
||||
for i in range(100):
|
||||
t=Thread(target=task)
|
||||
t.start()
|
||||
t.join()
|
||||
stop_time=time.time()
|
||||
print('主:%s n:%s' %(stop_time-start_time,n))
|
||||
```
|
||||
|
||||
### 死锁与递归锁
|
||||
|
||||
两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为**死锁进程**
|
||||
|
@@ -127,7 +127,7 @@ from greenlet import greenlet
|
||||
|
||||
def eat(name):
|
||||
print('%s eat 1' %name)
|
||||
g2.switch('aaron')
|
||||
g2.switch('张三')
|
||||
print('%s eat 2' %name)
|
||||
g2.switch()
|
||||
def play(name):
|
||||
@@ -138,7 +138,7 @@ def play(name):
|
||||
g1=greenlet(eat)
|
||||
g2=greenlet(play)
|
||||
|
||||
g1.switch('aaron') # 可以在第一次switch时传入参数,以后都不需要
|
||||
g1.switch('张三') # 可以在第一次switch时传入参数,以后都不需要
|
||||
```
|
||||
|
||||
单纯的切换(在没有io的情况下或者没有重复开辟内存空间的操作),反而会降低程序的执行速度
|
||||
@@ -224,8 +224,8 @@ def play(name):
|
||||
print('%s play 2' %name)
|
||||
|
||||
|
||||
g1=gevent.spawn(eat,'aaron')
|
||||
g2=gevent.spawn(play,name='aaron')
|
||||
g1=gevent.spawn(eat,'张三')
|
||||
g2=gevent.spawn(play,name='张三')
|
||||
g1.join()
|
||||
g2.join()
|
||||
#或者gevent.joinall([g1,g2])
|
||||
@@ -259,7 +259,7 @@ gevent.joinall([g1,g2])
|
||||
print('主')
|
||||
```
|
||||
|
||||
用threading.current_thread().getName()来查看每个g1和g2,查看的结果为DummyThread-n,即假线程
|
||||
用threading.current_thread().name来查看每个g1和g2,查看的结果为DummyThread-n,即假线程
|
||||
|
||||
```python
|
||||
from gevent import monkey;monkey.patch_all()
|
||||
@@ -267,13 +267,13 @@ import threading
|
||||
import gevent
|
||||
import time
|
||||
def eat():
|
||||
print(threading.current_thread().getName())
|
||||
print(threading.current_thread().name)
|
||||
print('eat food 1')
|
||||
time.sleep(2)
|
||||
print('eat food 2')
|
||||
|
||||
def play():
|
||||
print(threading.current_thread().getName())
|
||||
print(threading.current_thread().name)
|
||||
print('play 1')
|
||||
time.sleep(1)
|
||||
print('play 2')
|
||||
@@ -372,6 +372,8 @@ def talk(conn,addr):
|
||||
try:
|
||||
while True:
|
||||
res=conn.recv(1024)
|
||||
if len(res) == 0:
|
||||
continue
|
||||
print('client %s:%s msg: %s' %(addr[0],addr[1],res))
|
||||
conn.send(res.upper())
|
||||
except Exception as e:
|
||||
@@ -389,7 +391,7 @@ if __name__ == '__main__':
|
||||
from socket import *
|
||||
|
||||
client=socket(AF_INET,SOCK_STREAM)
|
||||
client.connect(('127.0.0.1',8080))
|
||||
client.connect(('127.0.0.1',8088))
|
||||
|
||||
|
||||
while True:
|
||||
@@ -414,7 +416,7 @@ def client(server_ip,port):
|
||||
|
||||
count=0
|
||||
while True:
|
||||
c.send(('%s say hello %s' %(threading.current_thread().getName(),count)).encode('utf-8'))
|
||||
c.send(('%s say hello %s' %(threading.current_thread().name,count)).encode('utf-8'))
|
||||
msg=c.recv(1024)
|
||||
print(msg.decode('utf-8'))
|
||||
count+=1
|
||||
|
Reference in New Issue
Block a user