Skip to content

Commit

Permalink
python-面向对象 - class
Browse files Browse the repository at this point in the history
  • Loading branch information
zq99299 committed May 9, 2024
1 parent de68547 commit fd24a0c
Show file tree
Hide file tree
Showing 3 changed files with 215 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/posts/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ module.exports = () => {
'others/018.md',
'others/019.md',
'others/020.md',
'others/021.md',
]
}
]
Expand Down
213 changes: 213 additions & 0 deletions docs/posts/others/021.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
# python-面向对象 - class
> 本文精选:https://www.yuque.com/mrcode.cn/note-combat/nv62kvczbzugm545
## 如何定义 class
在 Python 3.7 以前,需要这样写
```python
class TaskDemo:
def __init__(self, a: str, b: str, c: str = None):
self.a = a
self.b = b
self.c = c


if __name__ == '__main__':
task = TaskDemo('1', '2', 3)
task = TaskDemo('1', '2')
# 或者这样写
task = TaskDemo(a='1', b='2')
# 这个就会报错,因为是必须传入的值
task = TaskDemo('1')
```
在 Python 3.7+ ,可以使用 `@dataclass` 帮我们实习上面的效果,可以这样写,更方便

```python
from dataclasses import dataclass

@dataclass
class TaskDemo:
a: str
b: str
c: str = None
```
## 如何让一个 json 字符串变成类实例呢?
### object_hook 无嵌套对象反序列化
下面有一个例子
```python
import json
from dataclasses import dataclass

# """文档注释""",这种是文档注释,在类下面,字段下面
@dataclass
class TaskConfigData:
"""任务配置信息"""

redisKeyOfResult: str = None
"""响应结果存储在 Redis 中的 key"""

rawKeywords: str = None
"""原始关键词字符串"""

searchKeywords: list[str] = None
"""将原始关键词字符串,处理后的关键词列表"""

taskId: int = None
"""主任务ID"""

taskRecordId: int = None
"""当前任务ID"""

cookie_str: str = None
"""分配的账户对应的 cookie 字符串"""

spiderAccountId: int = None
"""分配的账户 ID"""

browser_headless: bool = True
"""是否开启无头模式"""

def to_json_str(self):
"""将对象转换为 json 字符串"""
return json.dumps(self, default=lambda o: o.__dict__, ensure_ascii=False)

# @staticmethod 注解是标记这个方法是一个静态方法
@staticmethod
def from_json_str(json_str):
"""
将 json 字符串转换为对象
这里使用 object_hook 参数来处理 TaskConfigData 对象,结果就是该类的实例
object_hook 就是遇到对象,就调用这个方法
"""
return json.loads(json_str, object_hook=lambda d: TaskConfigData(**d))
```
下面是测试
```python
def test_task_config_data():
config_data = {
"rawKeywords": "假发|潮流",
"redisKeyOfResult": "crawlab:task:result:1",
"searchKeywords": ["假发|潮流"],
"taskId": 1,
"taskRecordId": 1,
"cookie_str": 'did=web_81424f8dcdac46d8b31f2e75679ed311; didv=1712734359000; userId=2862954251; kuaishou.server.web_ph=fcfbbb6413ce14ca0670760dc2dec8d5fab5',
"spiderAccountId": 1,
"browser_headless": False, # 是否无头模式
}
# 从 json 字符串转换为对象
data: TaskConfigData = TaskConfigData.from_json_str(json.dumps(config_data))
print(data)
print(data.to_json_str())

if __name__ == '__main__':
test_task_config_data()

```
文档注释的效果如下:
![image.png](https://cdn.nlark.com/yuque/0/2024/png/651749/1715146290743-bf8e34c2-f31b-465a-91ca-76d599f0bdaa.png#averageHue=%23f7f7f7&clientId=u23ce6831-ae8e-4&from=paste&height=120&id=u7b4a0ab8&originHeight=221&originWidth=577&originalType=binary&ratio=2&rotation=0&showTitle=false&size=35696&status=done&style=none&taskId=uc8361871-28a0-42d5-8f0e-e28af64fefb&title=&width=312.5)
:::warning
需要注意的是:这种方式只适合没有嵌套对象的方式
:::
因为 object_hook 是遇到对象就回调,如果是嵌套对象就不行了,比如下面这种
```python
result = {
"taskId": 1,
"taskRecordId": 1,
"success": False,
"remark": "请求 API 失败",
"spiderAccountInfo": {
"id": 1,
"use": True,
"success": False,
"remark": "请求 API 失败"
}
}
```
他的解析方式如下:

1. 先拿 spiderAccountInfo 的值,也就是这个对象,去调用一次 object_hook
2. 再用 根 对象去调用一次 object_hook

因为在调用的时候,参数就是对象,比如
```python
{
"id": 1,
"use": True,
"success": False,
"remark": "请求 API 失败"
}
```
所以你就没法知道这个对象对应的字段是什么,在本例中是 spiderAccountInfo 的值,所以你就没有办法实现
### 处理有嵌套的对象
```python
@dataclass
class SpiderAccountInfo:
"""爬虫账号信息"""

id: str = None
"""分配的爬虫账号ID"""

use: bool = False
"""当次任务是否使用该账号"""

success: bool = False
"""使用账户请求 API 是否成功"""

remark: str = ''
"""备注信息,比如异常信息"""


@dataclass
class TaskResult:
taskId: str = None
"""主任务ID"""

taskRecordId: str = None
"""当前任务ID"""

success: bool = False
"""当前任务执行中是否有错误出现,比如异常信息"""

remark: str = None
"""备注信息"""

spiderAccountInfo: SpiderAccountInfo = None
"""爬虫账户相关信息"""

def to_json_str(self):
"""将对象转换为 json 字符串"""
return json.dumps(self, default=lambda o: o.__dict__, ensure_ascii=False)

@staticmethod
def from_json_str(json_str):
"""将 json 字符串转换为对象"""

json_dict = json.loads(json_str)
return TaskResult(**json_dict)
```
下面是测试
```python
def test_task_result():
result = {
"taskId": 1,
"taskRecordId": 1,
"success": False,
"remark": "请求 API 失败",
"spiderAccountInfo": {
"id": 1,
"use": True,
"success": False,
"remark": "请求 API 失败"
}
}
# 从 json 字符串转换为对象
data: TaskResult = TaskResult.from_json_str(json.dumps(result))
print(data)

if __name__ == '__main__':
test_task_result()
```
也就是说,一个 dict 对象,可以通过 `TaskResult(**json_dict)` 这种方式,直接赋值
:::warning
需要注意的是:如果给定一个该类不存在的字段,就会报错:TypeError: __init__() got an unexpected keyword argument 'taskId2'
:::

1 change: 1 addition & 0 deletions docs/posts/others/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
- [jackson JSON 框架](./018.md)
- [hutool 的 excel 文件解析增强工具](./019.md)
- [spring-boot-starter-data-redis](./020.md)
- [python-面向对象 - class](./021.md)

0 comments on commit fd24a0c

Please sign in to comment.