Python 如何实现一个和属性值相关的单例?

2020-05-26 10:18:47 +08:00
 huazhaozhe

假如某个类的实例比较耗费内存或者实例化的时候耗费较多的时间,因此想实现一个和实例属性相关的单例,不知道这么叫对不对,,,想达到的效果如下:

class Example():
    def __init__(self, attr1=None, attr2=None):
        self.attr1 = attr1
        self.attr2 = attr2

instance1 = Example('attr1', 'attr2')
instance2 = Example('attr1', 'attr2')
instance3 = Example('attr3', 'attr4')
instance4 = Example('attr1', 'attr3')

# 希望达到的效果
# instance1 is instance2 结果为 True
# instance1 is instance3 结果为 False
# instance1 is instance4 结果为 False

或者有什么其他更好的解决方法

2106 次点击
所在节点    Python
14 条回复
ruanimal
2020-05-26 10:20:24 +08:00
搞个字典做 cache 就 ok 了啊
huazhaozhe
2020-05-26 10:26:26 +08:00
@ruanimal 如果属性值较多那这个字典可能有好多层吧,,,,,而且有好几个这样的类他们属性不同就要写不同的代码和字典,所以有了这个想法
a719114136
2020-05-26 10:30:35 +08:00
重写 __eq__()方法,然后用 == 判断
ClericPy
2020-05-26 10:31:02 +08:00
Borg 模式? 缓存字典放到类属性里, 在 `__new__` 里做类似单例的事情
ClericPy
2020-05-26 10:33:47 +08:00
纠正下 #4

后半句说的是单例模式... new 里做的就是用元组 (attr1, attr2) 做 key 去缓存里找

上半句是说可以参考下 Borg 模式
cassidyhere
2020-05-26 10:36:26 +08:00
你需要创建缓存实例
python cookbook 里的例子:

import weakref

class Cached(type):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.__cache = weakref.WeakValueDictionary()

def __call__(self, *args):
if args in self.__cache:
return self.__cache[args]
else:
obj = super().__call__(*args)
self.__cache[args] = obj
return obj

# Example
class Spam(metaclass=Cached):
def __init__(self, name):
self.name = name


>>> a = Spam('Guido')
>>> b = Spam('Diana')
>>> c = Spam('Guido') # Cached
>>> a is b
False
>>> a is c # Cached value returned
True
islxyqwe
2020-05-26 10:42:08 +08:00
```python3
from functools import lru_cache
@lru_cache(None)
class Example():
def __init__(self, attr1=None, attr2=None):
self.attr1 = attr1
self.attr2 = attr2

instance1 = Example('attr1', 'attr2')
instance2 = Example('attr1', 'attr2')
instance3 = Example('attr3', 'attr4')
instance4 = Example('attr1', 'attr3')
print(instance1 is instance2,instance1 is instance3,instance1 is instance4)
```
结果:True False False
Vegetable
2020-05-26 10:46:22 +08:00
楼上正解,直接 lru_cache 完美符合你的需求,同样入参的调用会被缓存
huazhaozhe
2020-05-26 11:03:30 +08:00
@islxyqwe nice
huazhaozhe
2020-05-26 11:33:10 +08:00
@islxyqwe 当有默认参数的时候有点小问题,他是根据传入参数来决定的
noparking188
2020-05-26 11:36:03 +08:00
如果是数据相关的话,或许可以用下 dataclasses
ruanimal
2020-05-26 11:48:39 +08:00
@huazhaozhe 字典可以用 tuple 做 key,这样多少个属性都可以。 如果使用单例,最终也是用 dict 来缓存的。
huazhaozhe
2020-05-26 14:14:01 +08:00
@ruanimal 这样的话传入参数必须一样的顺序,否则就不是同一个 tuple
huazhaozhe
2020-05-26 14:15:51 +08:00
@ruanimal 而且当有默认参数时也有点问题,不过也可以用了,上边有人说的 lru_cache 可以直接用

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/675501

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX