作者zerof (猫橘毛发呆雕像)
看板Python
标题Re: [问题] 限制某个function只能被叫到一次
时间Tue Mar 16 17:36:49 2021
简单回一下你的误区。
1.) 的部分请见
https://git.io/JmZ9b
早一点的版本 (<3.6) 是直接用 None 来作 singleton, 在 Python 用 module scope 的
variables 做这种事还蛮常见的。
2.) 的部分
assign 之前要先用 global keyword 宣告成 global
在 multi-threading 的情况下,第 n 个 thread 有极大的可能在 assignment 完成之
前就进到 if condition, 也就是说client 的 creation 会被执行不只一次以上。
这情况蛮常见的,毕竟多数和 db 相关的 libs 都有直接或间接用到 C 会 release GIL
。
解法可参考上面 asyncio 作 thread-local 的方式,如果是想要 thread-global 的话一
般应该是用 multi-lock
(一时间想不到哪个 source code 里面有范例可以看,建议自己试一下)
※ 引述《ddavid (谎言接线生)》之铭言:
: ※ 引述《MaR1nlolz (mumimumi)》之铭言:
: : 最近遇到个问题,请问一下各位,
: : 当执行python app.py时,不论MongoClient()被呼叫几次,我希望create()只被呼叫
一
: : ,
: : 我目前的做法是透过global variable来判断
: : create()是否被呼叫过,不过global variable用法几乎都不太建议使用,
: : 避免被其他地方改到,想请问各位有没有比较好的做法,以下是我的程式码,谢谢
: 我觉得根本问题是你的使用方式出了问题:
: 1. instance本质上也不应该global使用
: 2. 你这段Code里面有过多不必要的包装
: 先跳过问题1。针对问题2,你会发现这里的isInit == False几乎等价於
: instance is None(除非很例外的情况导致MongoClient传回None),也就是这是多
: 余的逻辑包装。最简单的方式是:
: --- mongo.py
: from pymongo import MongoClient
: instance = None
: def create():
: if instance is None:
: instance = MongoClient(
: 'mongo://127.0.0.1:27017',
: maxPoolSize=10
: )
: --- app.py
: import mongo
: mongo.create() # 之後拿 mongo.instance 来运用
: mongo.create() # 重复呼叫create()会因为instance已经不是None而不会做任何事
: 事实上我想不太到为什麽会导致重复create的状态,照理说程式应该开头create
: 一次後就只用create好的实体,除非断线否则再也不会呼叫第二次create才对。你如
: 果把问题一也解决,就会发现连create(或者说整个mongo.py)都是多余包装。
: : --- mongo.py
: : instance = None
: : isInit = False
: : def create():
: : mogno = MongoClient('mongo://127.0.0.1:27017', maxPoolSize=10)
: : return mogno
: : def mongoClient():
: : global isInit, instance
: : if isInit == False:
: : instance = create()
: : isInit = True
: : --- app.py
: : import mongoClient
: : mongoClient()
: : mongoClient()
: : mongoClient()
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 180.218.219.240 (台湾)
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/Python/M.1615887411.A.F99.html
※ 编辑: zerof (180.218.219.240 台湾), 03/16/2021 17:41:04
1F:→ zerof: 用 mobile app 发文排版有点怪就将就点 03/16 17:42
2F:→ zerof: lock 的部份关键字找『双重检查锁定模式』自己研究一下8 03/16 17:58
3F:推 ddavid: 我完全同意你说的内容,但就原Po明白摆出的Code而言,端 03/17 05:59
4F:→ ddavid: 出这篇的内容是否有种XY Problem的感觉?XD 03/17 05:59
5F:推 ddavid: 比如原问如果明白讲了在multi-threading,我自然就不会这 03/17 06:03
6F:→ ddavid: 麽答了,但他明白就在问app.py直接call了三次XD 03/17 06:03
7F:推 ddavid: 1的部分亦同。当然你的补充都是很正确,我单纯在原问有所 03/17 06:09
8F:→ ddavid: 补充设定以前就先这麽答,待他若不满意而有所补充,自然 03/17 06:09
9F:→ ddavid: 你就华丽登场了XD 03/17 06:09
打字没办法表示语气,有觉得冒犯的话先说个抱歉。
就原文的部份,我的看法是 1F 给的 singleton & after_create hook 这两个关键字
就足以解决他的问题了。(当然他自己写的 code 的 quality 是另一回事)
(以他的档名`app.py` + db initial 的情况,没意外会是 flask-like 的 web)
基本上我回文主要是基於你的内文 (会提到 multi-thread 的部份,一来是 link 中
的 `_RunningLoop` 是继承自 threading.local ,二来是你原文内对 create method
「想不太到会导致重复」的补充说明。)
btw, link 稍微更新了一下,之前好像缩的时候没缩到行号,在 L:694 左右。
当然我也不单纯是无聊想纠正你,在假设他是用 flask 的情况下,在 local debug 的
时候就可以用 `app.run(threaded=True)` 来模拟(?) production 的情况,在文件内提
供的多数 deploy options (gevent, twisted, gunicorn...) 内都会有 multithreading
的 context (如果是用 Sanic 的话就更复杂了但我想会 Sanic 的不会问这种问题)
这些内容基本上用他用的 framework 当 keyword 加上 singleton/after_create hook
都会有些类似的 stackoverflow 可以研究参考。
(不过 flask 没有 after_create hook ,当然你没说我也没必要特别提供正确的...
呃不是,就 keyword 自己研究下先搞清楚自己在问什麽说不定就解决了嘛)
我自己的想法很简单,你懒我也懒,资讯给得少我就回少一点…会在网路上问问题的
人我自己是归类成两种,一种会符合 SSCCE(
http://www.sscce.org/ ),要回问题就很
快;另一种就是给的通常都是片段的资讯,有时候自己也不知道自己在问什麽 (这种连
吐嘈都觉得好懒啊...)。
除此之外的共通点就是,会再给更多资讯的很少。 (有时候是自己想通了,有时候是
看了别人给的建议反而做了别的选择之类的 blahblah)
这种行为就像是在丢 UDP 的 packets 一样,没有下文。
(反正我问题解了就好,回个感谢或是什麽之类的就再说)
也无所谓啦,反正我也是那种兴致来了就回个文, keywords 都有大家自己研究研究
大概是这样。最後还是回到一开头那句,看文的觉得有冒犯就抱歉惹误会误会><
※ 编辑: zerof (180.218.219.240 台湾), 03/18/2021 02:31:29
10F:推 ddavid: 完全没问题!:) 03/18 08:04
11F:→ ToastBen: 误区是哪里的方言? 03/18 18:57
12F:→ zerof: 误区一词常见於商院相关书籍,英文应该是用 area of misund 03/19 00:07
13F:→ zerof: erstanding; 台文比较接近的词应该是 誖误 (但实际上和誖误 03/19 00:07
14F:→ zerof: 的意思有些出入) 中文有蛮多词在萌典/教育部辞典是查不到 03/19 00:07
15F:→ zerof: 的。至於是否为中国惯用词汇,希望你可以研究一下 我也蛮想 03/19 00:07
16F:→ zerof: 知道的 ;) 03/19 00:07
17F:→ kobe8112: 誖误不是有些出入,是完全不同吧... 03/19 09:09
18F:→ kobe8112: 就单纯积非成是而已 03/19 09:09
19F:→ zerof: 个人建议就还是...查查字典 03/19 20:28