Python:import语句符号差异引发的对象标识断言

2024-04-19 13:06:27 发布

您现在位置:Python中文网/ 问答频道 /正文

在检查对象的标识时,我得到了断言错误,因为对象创建代码以一种表示法(base.other_stuff.BarCode)导入对象定义模块,而标识检查代码以不同的表示法(other_stuff.BarCode)导入相同的模块。(请看下面血淋淋的细节。)

似乎isinstance()调用对对象定义模块的引用很敏感,希望它以完全相同的表示法导入。(我使用的是2.5版。)

我想可以通过在检查标识的代码中更改导入符号来解决这个问题,但是我担心我会把同样的问题传播到依赖它的其他代码中。我肯定有一些更优雅的解决方案,我可能应该首先使用。你知道吗

那我该怎么解决这个问题呢?你知道吗

详细信息

Python路径:“/”,“/base/”

文件:

/__init__.py
base/__init__.py
base/other_stuff/__init__.py
base/other_stuff/BarCode.py
base/stuff/__init__.py
camp/__init__.py

基础/材料文本/食品代码.py地址:

import other_stuff.BarCode as bc

class Foo:
    def __init__(self, barThing):
        assert isinstance(barThing, bc.Bar)

营地文本/新_代码.py地址:

import base.stuff.FooCode as fc
import base.other_stuff.BarCode as bc

thisBar = bc.Bar()
assert isinstance(thisBar, bc.Bar)
thisFoo = fc.Foo(barThing=thisBar)

这失败了。它通过了断言测试,但在初始代码中的断言失败了。你知道吗

但是,当我修改要导入的新代码时,它可以工作条形码.py使用:

import other_stuff.BarCode as bc

。因为所有的东西都在Python身上。你知道吗


Tags: 模块对象代码pyimportbaseinitas
3条回答

你的代码布局被严重破坏了。在sys.path中不应该有包目录。你知道吗

在这种情况下,Python将使用两种不同的搜索路径来查找BarCode.py,因此将它作为单独的模块bar.other_stuff.BarCodeother_stuff.BarCode加载两次。这意味着此模块中的每个对象都存在两次,浪费内存,并且对象标识自然会失败:

>>> from base.other_stuff import BarCode as bc1
>>> from other_stuff import BarCode as bc2
>>> bc1
<module 'base.other_stuff.BarCode' from '.../base/other_stuff/BarCode.pyc'>
>>> bc2
<module 'other_stuff.BarCode' from '.../other_stuff/BarCode.pyc'>
>>> bc1 == b2
False
>>> bc1 is bc2
False

尽管它们来自同一个源文件,但Python将bc1bc2视为不同的模块。你知道吗

确保您使用的每个模块都可以通过其完整限定名进行唯一标识,在您的示例中:base.other_stuff.BarCode如果模块是包的一部分,请不要将包目录添加到sys.path。你知道吗

“符号”是最少的问题——定义为在语义上引用同一模块的不同符号可以保证生成相同的对象。例如:

>>> import sys as foobar
>>> import sys as zapzip
>>> foobar is zapzip
True

问题是,确实有可能多次导入同一个文件,这种导入方式不会让导入机制完全知道您在做什么,从而导致不同的模块对象。例如,像您正在使用的重叠路径很容易产生这种情况。你知道吗

一种方法(如果您坚持要编写代码和/或布局文件系统,可能会产生混淆/误导;-)是将__builtin__.__import__设置为您自己的函数,在调用以前的/正常版本之后,检查新导入模块的__file__属性与sys.modules中已有的属性(值得维护这些属性的dict,将文件映射到该文件的规范模块对象)使用操作系统路径(或者更强大的方法来检测单个文件的同义词,例如符号链接和硬链接,通过标准库模块os)中的功能。你知道吗

通过这个钩子,您可以确保任何单个给定文件的所有导入都将始终导致一个规范化的模块对象,几乎不管路径和文件系统中发生什么旋转(聪明的攻击者仍然可以通过安装自己设计的复杂文件系统来挫败检查,但我不这么认为)认为你实际上是在试图防范蓄意的狡猾攻击;-)。你知道吗

看起来你的<root>/<root>/base中有sys.path,这总是不好的。当你从base/stuff做import other_stuff.BarCode as bc/食品代码.py它将other_stuff作为根包导入,而不是base的子包。因此,在执行import base.other_stuff.BarCode as bc之后,将BarCode模块导入两次:使用other_stuff.BarCodebase.other_stuff.BarCode。你知道吗

最好的解决方案是:

  1. sys.path(或$PYTHONPATH)中删除<root>/base。你知道吗
  2. 在base/stuff中使用相对导入/食品代码.py:from ..other_stuff import BarCode as bc。你知道吗

相关问题 更多 >