Python :如何避免某个函数内的类反复进行实例化?

2021-02-27 23:38:15 +08:00
 capbone

假设有一个包含类 C 实例的函数 foo

def foo(x):
    bar = C()
    return bar(x)

foo 这个函数会被某个业务反复调用(比如每分钟 10,000 次),有什么办法能够让类 C 的实例化过程(bar = C())只在第一次被调用的时候执行一次呢?换句话说,第一次调用生成 bar 对象后,如何把它缓存住,让第二次及后续调用都直接载入这个缓存对象呢?

有几个限制:

1 、C 是在某个第三方库中实现的,因此没法修改源码让 C 成为单例;

2 、函数 foo 的参数格式不允许修改;

3 、foo 可能会被 a.py 调用,也可能被 b.py 调用,我希望在这两个 py 文件执行的过程中 bar 对象是独立存在的,因此好像不适合把 foo 定义为 global

如果暂且不考虑限制 3,有没有什么比较好的方法实现?

2630 次点击
所在节点    Python
12 条回复
ericls
2021-02-27 23:55:12 +08:00
```
class C:
def __init__(self):
print("here")



c_cache = {}
def make_c():
import inspect
frame = inspect.stack()
key = '.'.join([frame[1].filename, frame[1].function])
if key not in c_cache:
c_cache[key] = C()
return c_cache[key]


def foo():
c = make_c()


def bar():
c = make_c()


for i in range(100):
foo()

for i in range(100):
bar()
```
ericls
2021-02-27 23:56:45 +08:00
nightwitch
2021-02-28 00:03:41 +08:00
https://stackoverflow.com/questions/279561/what-is-the-python-equivalent-of-static-variables-inside-a-function

参考最高赞。Python 里面函数也是 Object,所以可以给函数添加一个属性
imn1
2021-02-28 00:04:32 +08:00
如果 x 的变化不大,直接 @lur_cache,都懒得运行了
hsfzxjy
2021-02-28 00:10:09 +08:00
C 的实例化单独拎一个函数,然后套上 lru_cache
BiteTheDust
2021-02-28 00:10:37 +08:00
把函数封装到一个对象里
ipwx
2021-02-28 00:15:26 +08:00
foo 可能会被 a.py 调用,也可能被 b.py 调用,我希望在这两个 py 文件执行的过程中 bar 对象是独立存在的,因此好像不适合把 foo 定义为 global ?
----

a.pyb.py 里面分别建立一个 foo 的不同闭包不就行了。。。



def make_foo():
....bar = C()
....def foo(x):
........return bar(x)


a.py:

foo = make_foo()


b.py:

foo = make_foo()
ipwx
2021-02-28 00:17:05 +08:00
顺便如果是对象的话重载 __call__ 就能看上去是函数了。

class Foo(object):
....def __init__(self):
........self.bar = C()
....def __call__(self, x):
........return self.bar(x)

a. py:

foo = Foo()

b. py:

foo = Foo()
capbone
2021-02-28 18:54:26 +08:00
@hsfzxjy 如果不同模块分别调用,lru_cache 只会缓存一份吧?限制 3 似乎不满足?
hsfzxjy
2021-02-28 23:35:38 +08:00
@capbone 那你可以加一个参数,调用时把调用者的__file__传进去
leafcoder
2021-03-01 10:10:38 +08:00
忽略第 3 条条件可以如下:

class C(object):
pass


class Demo(object):

def __new__(cls, *args, **kwargs):
inst = object.__new__(cls, *args, **kwargs)
if not hasattr(Demo, 'c_inst'):
c_inst = C(*args, **kwargs)
Demo.c_inst = c_inst
inst.c_inst = Demo.c_inst
return inst

def __init__(self, *args, **kwargs):
pass


for i in range(5):
o = Demo()
print(o, o.c_inst)

输出:

>>> (<__main__.Demo object at 0x7f7bd9e95310>, <__main__.C object at 0x7f7bd9e95350>)
>>> (<__main__.Demo object at 0x7f7bd9e95390>, <__main__.C object at 0x7f7bd9e95350>)
>>> (<__main__.Demo object at 0x7f7bd9e95310>, <__main__.C object at 0x7f7bd9e95350>)
>>> (<__main__.Demo object at 0x7f7bd9e95390>, <__main__.C object at 0x7f7bd9e95350>)
>>> (<__main__.Demo object at 0x7f7bd9e95310>, <__main__.C object at 0x7f7bd9e95350>)
no1xsyzy
2021-03-01 12:49:24 +08:00
@ipwx 在 #8 基础上可以把 bar 写成一个 functools.cached_property,可以避免过早求值。

@capbone 限制 3 按 pep8 应当更显式地书写

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

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

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

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

© 2021 V2EX