# python-面向对象 - class

本文精选:https://www.yuque.com/mrcode.cn/note-combat/nv62kvczbzugm545

# 如何定义 class

在 Python 3.7 以前,需要这样写

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')
1
2
3
4
5
6
7
8
9
10
11
12
13
14

在 Python 3.7+ ,可以使用 @dataclass 帮我们实习上面的效果,可以这样写,更方便

from dataclasses import dataclass

@dataclass
class TaskDemo:
    a: str
    b: str
    c: str = None
1
2
3
4
5
6
7

# 如何让一个 json 字符串变成类实例呢?

# object_hook 无嵌套对象反序列化

下面有一个例子

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))
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

下面是测试

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()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

文档注释的效果如下: image.png

WARNING

需要注意的是:这种方式只适合没有嵌套对象的方式

因为 object_hook 是遇到对象就回调,如果是嵌套对象就不行了,比如下面这种

    result = {
        "taskId": 1,
        "taskRecordId": 1,
        "success": False,
        "remark": "请求 API 失败",
        "spiderAccountInfo": {
            "id": 1,
            "use": True,
            "success": False,
            "remark": "请求 API 失败"
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12

他的解析方式如下:

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

因为在调用的时候,参数就是对象,比如

{
    "id": 1,
    "use": True,
    "success": False,
    "remark": "请求 API 失败"
}
1
2
3
4
5
6

所以你就没法知道这个对象对应的字段是什么,在本例中是 spiderAccountInfo 的值,所以你就没有办法实现

# 处理有嵌套的对象

@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)
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

下面是测试

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()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

也就是说,一个 dict 对象,可以通过 TaskResult(**json_dict) 这种方式,直接赋值

WARNING

需要注意的是:如果给定一个该类不存在的字段,就会报错:TypeError: init() got an unexpected keyword argument 'taskId2'