File size: 4,040 Bytes
80bf15d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import inspect


class ParamsSingleton(object):
    """根据传入的参数不同而创建单例.
    由于参数中可能包含字典, 如果转字符串的话, 字典的 key 是无序的.
    所以用了列表而不是字典来存实例. """
    __instance = list()
    _initialized = False

    def __new__(cls, *args, **kwargs):
        kwargs = cls.to_kwargs(*args, **kwargs)
        kwargs['cls'] = cls

        for obj, params in cls.__instance:
            if params == kwargs:
                return obj

        obj = super().__new__(cls)
        # 让每个类实例, 可以拿到自己的 kwargs
        # setattr(obj, 'kwargs', kwargs)
        obj.kwargs = kwargs
        cls.__instance.append((obj, kwargs))
        return obj

    @classmethod
    def get_all_instance(cls) -> list:
        return cls.__instance

    @classmethod
    def to_kwargs(cls, *args, **kwargs):
        """将传入 __init__ 的参数全部转为 key-value 字典的关键字参数"""

        # 获取当前传入参数值.
        argvalues = inspect.getargvalues(inspect.currentframe())
        args = list(argvalues.locals['args'])
        kwargs = argvalues.locals['kwargs']
        for k, v in argvalues.locals.items():
            if k in ('cls', 'args', 'kwargs'):
                continue
            else:
                kwargs[k] = v

        # 获取函数接受哪些参数.
        fullargspec = inspect.getfullargspec(cls.__init__)
        # 函数的参数分为已知的位置参数, 未知的位置参数, 已知的关键字参数, 未知的关键字参数.
        # 在 `未知的位置参数` 之前的参数都是 `已知的位置参数`. 它们可能有默认值
        # 有默认值的参数并不都是关键字参数. 关键字参数也可以没有默认值.

        # fullargspec.args: `已知的位置参数` 的名称的列表.
        # fullargspec.defaults: 元组或None. `已知的位置参数` 中最后几项的默认值.
        # fullargspec.kwonlyargs: `已知的关键字参数` 的名称列表 (没有默认值的关键字参数, 是必须要传入的).
        # fullargspec.kwonlydefaults: `已知的关键字参数` 的默认值.

        arg_name_list = fullargspec.args

        # 将未被赋值 `已知的位置参数` 的默认值写入 kwargs.
        if fullargspec.defaults is not None:
            l = len(fullargspec.defaults)
            default_args = fullargspec.args[-l:]
            for k, v in zip(default_args, fullargspec.defaults):
                if k in kwargs:
                    continue
                else:
                    kwargs[k] = v

        # 将 `已知关键字参数` 的默认值写入 kwargs.
        if fullargspec.kwonlydefaults is not None:
            for k, v in fullargspec.kwonlydefaults.items():
                if k in kwargs:
                    continue
                else:
                    kwargs[k] = v

        # if fullargspec.kwonlyargs is not None:
        #     arg_name_list.extend(fullargspec.kwonlyargs)
                kwargs = dict()
        for arg_name in arg_name_list:
            if arg_name == 'self':
                continue
            try:
                value = args.pop(0)
            except IndexError:
                break
            kwargs[arg_name] = value

        if fullargspec.varargs is not None:
            kwargs[fullargspec.varargs] = tuple(args)

        return kwargs

    @classmethod
    def flush(cls):
        cls.__instance = list()
        return


def demo1():
    class A(ParamsSingleton):
        pass

    class B(A):
        # def __init__(self, name, *args1, age, **kwargs):
        def __init__(self, name, age=27, **kwargs):

            pass

    b1 = B('jack')
    print('-' * 25)
    # b2 = B('jack', 1, 2, age=25, **{'high': 165})
    # print('-' * 25)
    b3 = B(name='jack', **{'age': 25, 'high': 165})
    # b3 = B(name='jack', **{'high': 165})

    print('-' * 25)

    # print(b1)
    return


if __name__ == '__main__':
    demo1()