Python 具有不同导入策略的可变类属性
我最近偶然发现了可变类属性的一种奇怪行为。Python 具有不同导入策略的可变类属性,python,python-3.6,Python,Python 3.6,我最近偶然发现了可变类属性的一种奇怪行为。 理论上(实际上),在类级别定义的可变参数将在所有实例中共享,直到任何实例覆盖引用为止 在我们的项目中,我们有如下目录结构: ├─── main.py └─── src ├─── __init__.py ├─── cache_user.py └─── common_cache.py 公共_cache.py: cache_user.py: main.py: 运行main.py的结果: 所有ID都与预期的相同,这证明所有实例和类都共
理论上(实际上),在类级别定义的可变参数将在所有实例中共享,直到任何实例覆盖引用为止 在我们的项目中,我们有如下目录结构:
├─── main.py
└─── src
├─── __init__.py
├─── cache_user.py
└─── common_cache.py
公共_cache.py:
cache_user.py:
main.py:
运行main.py的结果:
所有ID都与预期的相同,这证明所有实例和类都共享对同一字典的公共引用。然而,当我们将src
目录添加到PYTHONPATH
并稍微更改导入时,会发生一些奇怪的事情
cache_user.py:
main.py
运行main.py的结果:
现在,CommonCache
(和实例)有了自己的cache
字典,不同于CacheUser
(和实例)。怎么搞的?唯一改变的是我们进行导入的方法。若我们将断点放在CommonCache
类中,我们可以清楚地看到,cache={}
执行两次-每次导入一次。如果有多个类导入CommonCache
,则只有不同的导入方法才会触发cache={}
在我们的项目中解决这个问题不仅仅是统一导入方法,因为项目太大了,无法做到这一点。我们的解决方案是创建只由CommonCache
导入的CacheStorage
类,真正的cache
dict存储在CacheStorage
中
那么,为什么会这样?我找不到这种情况的任何解释。还有,如何处理?有没有比创建
CacheStorage
对象更好的解决方案
这在CentOS和Windows上的Python3.6中进行了测试。这表明模块是通过其“导入”名称而不是模块文件的绝对文件来标识的。在您的情况下,一个模块
src.common\u cache
位于
中,而另一个模块common\u cache
位于/src
中
。因此,导入了两个不同的模块(Python解释器的想法):
- 一个
在模块CommonCache
src.common\u cache
- 在模块
中定义的一个common\u cache
CommonCache
CommonCache
未引用同一类
assert src.common_cache.CommonCache is not common_cache.CommonCache
我认为
src.common_cache
和common_cache
虽然指向同一个文件,但它们被认为是不同的模块。为了归档类变量/模块变量的单例模式,应该在相关文件中标准化如何导入模块的名称。但主要的问题是为什么会这样?这很清楚。但是,如果两种情况下的文件路径相同,您能告诉我为什么它会以这种方式工作吗?它是一个bug还是一个特性?如果是功能,用例是什么?这不是bug,也不是功能。这就是导入系统的工作方式。Python首先在内存中搜索所需的,以查看是否已加载。如果没有,Python将在sys.path
(与PYTHONPATH
相关)中逐个搜索它,或者提升importorror
。好吧,但是为什么src.common\u cache
和common\u cache
的目标是同一个文件时,它们会被视为不同的模块呢?您可以在sys.modules
中看到这一点。这看起来像个虫子。注意,在第一个示例中,import语句只执行一次。永远不会引发ImportError
,如果同一文件位于PYTHONPATH
中,它只会导入两次。如果sys.modules
中的键是完整文件路径而不是相对路径,则两个模块将被视为一个。
from .common_cache import CommonCache
class CacheUser(CommonCache):
pass
from src.cache_user import CacheUser
from src.common_cache import CommonCache
common_cache = CommonCache()
cache_user = CacheUser()
print(id(common_cache.cache))
print(id(CommonCache.cache))
print(id(cache_user.cache))
print(id(CacheUser.cache))
2163305605304
2163305605304
2163305605304
2163305605304
from src.common_cache import CommonCache
class CacheUser(CommonCache):
pass
from cache_user import CacheUser
from common_cache import CommonCache
common_cache = CommonCache()
cache_user = CacheUser()
print(id(common_cache.cache))
print(id(CommonCache.cache))
print(id(cache_user.cache))
print(id(CacheUser.cache))
2067930459016
2067930459016
2067930458440
2067930458440
assert src.common_cache.CommonCache is not common_cache.CommonCache