V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
capbone
V2EX  ›  Python

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

  •  
  •   capbone ·
    qiujueqin · 2021-02-27 23:38:15 +08:00 · 2458 次点击
    这是一个创建于 1125 天前的主题,其中的信息可能已经有所发展或是发生改变。

    假设有一个包含类 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,有没有什么比较好的方法实现?

    12 条回复    2021-03-01 12:49:24 +08:00
    ericls
        1
    ericls  
       2021-02-27 23:55:12 +08:00   ❤️ 1
    ```
    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
        2
    ericls  
       2021-02-27 23:56:45 +08:00   ❤️ 1
    nightwitch
        3
    nightwitch  
       2021-02-28 00:03:41 +08:00   ❤️ 1
    https://stackoverflow.com/questions/279561/what-is-the-python-equivalent-of-static-variables-inside-a-function

    参考最高赞。Python 里面函数也是 Object,所以可以给函数添加一个属性
    imn1
        4
    imn1  
       2021-02-28 00:04:32 +08:00
    如果 x 的变化不大,直接 @lur_cache,都懒得运行了
    hsfzxjy
        5
    hsfzxjy  
       2021-02-28 00:10:09 +08:00 via Android
    C 的实例化单独拎一个函数,然后套上 lru_cache
    BiteTheDust
        6
    BiteTheDust  
       2021-02-28 00:10:37 +08:00
    把函数封装到一个对象里
    ipwx
        7
    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
        8
    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
        9
    capbone  
    OP
       2021-02-28 18:54:26 +08:00
    @hsfzxjy 如果不同模块分别调用,lru_cache 只会缓存一份吧?限制 3 似乎不满足?
    hsfzxjy
        10
    hsfzxjy  
       2021-02-28 23:35:38 +08:00 via Android
    @capbone 那你可以加一个参数,调用时把调用者的__file__传进去
    leafcoder
        11
    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
        12
    no1xsyzy  
       2021-03-01 12:49:24 +08:00
    @ipwx 在 #8 基础上可以把 bar 写成一个 functools.cached_property,可以避免过早求值。

    @capbone 限制 3 按 pep8 应当更显式地书写
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   4570 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 09:56 · PVG 17:56 · LAX 02:56 · JFK 05:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.