File size: 9,875 Bytes
96d7ad8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
from copy import deepcopy
from typing import List, Tuple, Dict

from mmcm.utils.str_util import has_key_brace


class BaseAttribute2Text(object):
    """
    属性转化为文本的基类,该类作用就是输入属性,转化为描述文本。
    Base class for converting attributes to text which converts attributes to prompt text.
    """

    name = "base_attribute"

    def __init__(self, name: str = None) -> None:
        """这里类实例初始化设置`name`参数,主要是为了便于一些没有提前实现、通过字符串参数实现的新属性。
            Theses class instances are initialized with the `name` parameter to facilitate the implementation of new attributes that are not implemented in advance and are implemented through string parameters.

        Args:
            name (str, optional): _description_. Defaults to None.
        """
        if name is not None:
            self.name = name

    def __call__(self, attributes) -> str:
        raise NotImplementedError


class AttributeIsTextAndName(BaseAttribute2Text):
    """
    属性文本转换功能类,将key和value拼接在一起作为文本.
    class for converting attributes to text which concatenates the key and value together as text.
    """

    name = "attribute_is_text_name"

    def __call__(self, attributes) -> str:
        if attributes == "" or attributes is None:
            return ""
        attributes = attributes.split(",")
        text = ", ".join(
            [
                "{} {}".format(attr, self.name) if attr != "" else ""
                for attr in attributes
            ]
        )
        return text


class AttriributeIsText(BaseAttribute2Text):
    """
    属性文本转换功能类,将value作为文本.
    class for converting attributes to text which only uses the value as text.
    """

    name = "attribute_is_text"

    def __call__(self, attributes: str) -> str:
        if attributes == "" or attributes is None:
            return ""
        attributes = str(attributes)
        attributes = attributes.split(",")
        text = ", ".join(["{}".format(attr) for attr in attributes])
        return text


class MultiAttr2Text(object):
    """将多属性组成的字典转换成完整的文本描述,目前采用简单的前后拼接方式,以`, `作为拼接符号
    class for converting a dictionary of multiple attributes into a complete text description. Currently, a simple front and back splicing method is used, with `, ` as the splicing symbol.

    Args:
        object (_type_): _description_
    """

    def __init__(self, funcs: list, name) -> None:
        """
        Args:
            funcs (list): 继承`BaseAttribute2Text`并实现了`__call__`函数的类. Inherited `BaseAttribute2Text` and implemented the `__call__` function of the class.
            name (_type_): 该多属性的一个名字,可通过该类方便了解对应相关属性都是关于啥的。 name of the multi-attribute, which can be used to easily understand what the corresponding related attributes are about.
        """
        if not isinstance(funcs, list):
            funcs = [funcs]
        self.funcs = funcs
        self.name = name

    def __call__(
        self, dct: dict, ignored_blank_str: bool = False
    ) -> List[Tuple[str, str]]:
        """
        有时候一个属性可能会返回多个文本,如 style cartoon会返回宫崎骏和皮克斯两种风格,采用外积增殖成多个字典。
        sometimes an attribute may return multiple texts, such as style cartoon will return two styles, Miyazaki and Pixar, which are multiplied into multiple dictionaries by the outer product.
        Args:
            dct (dict): 多属性组成的字典,可能有self.funcs关注的属性也可能没有,self.funcs按照各自的名字按需提取关注的属性和值,并转化成文本.
                Dict of multiple attributes, may or may not have the attributes that self.funcs is concerned with. self.funcs extracts the attributes and values of interest according to their respective names and converts them into text.
            ignored_blank_str (bool): 如果某个attr2text返回的是空字符串,是否要过滤掉该属性。默认`False`.
                If the text returned by an attr2text is an empty string, whether to filter out the attribute. Defaults to `False`.
        Returns:
            Union[List[List[Tuple[str, str]]], List[Tuple[str, str]]: 多组多属性文本字典列表. Multiple sets of multi-attribute text dictionaries.
        """
        attrs_lst = [[]]
        for func in self.funcs:
            if func.name in dct:
                attrs = func(dct[func.name])
                if isinstance(attrs, str):
                    for i in range(len(attrs_lst)):
                        attrs_lst[i].append((func.name, attrs))
                else:
                    # 一个属性可能会返回多个文本
                    n_attrs = len(attrs)
                    new_attrs_lst = []
                    for n in range(n_attrs):
                        attrs_lst_cp = deepcopy(attrs_lst)
                        for i in range(len(attrs_lst_cp)):
                            attrs_lst_cp[i].append((func.name, attrs[n]))
                        new_attrs_lst.extend(attrs_lst_cp)
                    attrs_lst = new_attrs_lst

        texts = [
            [
                (attr, text)
                for (attr, text) in attrs
                if not (text == "" and ignored_blank_str)
            ]
            for attrs in attrs_lst
        ]
        return texts


def format_tuple_texts(template: str, texts: Tuple[str, str]) -> str:
    """使用含有"{}" 的模板对多属性文本元组进行拼接,形成新文本
        concatenate multiple attribute text tuples using a template containing "{}" to form a new text
    Args:
        template (str):
        texts (Tuple[str, str]): 多属性文本元组. multiple attribute text tuples

    Returns:
        str: 拼接后的新文本, merged new text
    """
    merged_text = ", ".join([text[1] for text in texts if text[1] != ""])
    merged_text = template.format(merged_text)
    return merged_text


def format_dct_texts(template: str, texts: Dict[str, str]) -> str:
    """使用含有"{key}" 的模板对多属性文本字典进行拼接,形成新文本
        concatenate multiple attribute text dictionaries using a template containing "{key}" to form a new text
    Args:
        template (str):
        texts (Tuple[str, str]): 多属性文本字典. multiple attribute text dictionaries

    Returns:
        str: 拼接后的新文本, merged new text
    """
    merged_text = template.format(**texts)
    return merged_text


def merge_multi_attrtext(texts: List[Tuple[str, str]], template: str = None) -> str:
    """对多属性文本元组进行拼接,形成新文本。
        如果`template`含有{key},则根据key来取值;
        如果`template`有且只有1个{},则根据先后顺序对texts中的值进行拼接。

        concatenate multiple attribute text tuples to form a new text.
        if `template` contains {key}, the value is taken according to the key;
        if `template` contains only one {}, the values in texts are concatenated in order.
    Args:
        texts (List[Tuple[str, str]]): Tuple[str, str]第一个str是属性名,第二个str是属性转化的文本.
            Tuple[str, str] The first str is the attribute name, and the second str is the text of the attribute conversion.
        template (str, optional): template . Defaults to None.

    Returns:
        str: 拼接后的新文本, merged new text
    """
    if not isinstance(texts, List):
        texts = [texts]
    if template is None or template == "":
        template = "{}"
    if has_key_brace(template):
        texts = {k: v for k, v in texts}
        merged_text = format_dct_texts(template, texts)
    else:
        merged_text = format_tuple_texts(template, texts)
    return merged_text


class PresetMultiAttr2Text(MultiAttr2Text):
    """预置了多种关注属性转换的类,方便维护
    class for multiple attribute conversion with multiple attention attributes preset for easy maintenance

    """

    preset_attributes = []

    def __init__(
        self, funcs: List = None, use_preset: bool = True, name: str = "preset"
    ) -> None:
        """虽然预置了关注的属性列表和转换类,但也允许定义示例时,进行更新。
        注意`self.preset_attributes`的元素只是类名字,以便减少实例化的资源消耗。而funcs是实例化后的属性转换列表。

        Although the list of attention attributes and conversion classes is preset, it is also allowed to be updated when defining an instance.
        Note that the elements of `self.preset_attributes` are only class names, in order to reduce the resource consumption of instantiation. And funcs is a list of instantiated attribute conversions.

        Args:
            funcs (List, optional): list of funcs . Defaults to None.
            use_preset (bool, optional): _description_. Defaults to True.
            name (str, optional): _description_. Defaults to "preset".
        """
        if use_preset:
            preset_funcs = self.preset()
        else:
            preset_funcs = []
        if funcs is None:
            funcs = []
        if not isinstance(funcs, list):
            funcs = [funcs]
        funcs_names = [func.name for func in funcs]
        preset_funcs = [
            preset_func
            for preset_func in preset_funcs
            if preset_func.name not in funcs_names
        ]
        funcs = funcs + preset_funcs
        super().__init__(funcs, name)

    def preset(self):
        funcs = [cls() for cls in self.preset_attributes]
        return funcs