代码之家  ›  专栏  ›  技术社区  ›  chernevik

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

  •  1
  • chernevik  · 技术社区  · 15 年前

    当检查对象的标识时,我会得到断言错误,因为对象创建代码在一个符号下导入了对象定义模块。( base.other_stuff.BarCode )身份检查代码以不同的符号导入同一个模块( other_stuff.BarCode )(请参阅下面的血腥细节。)

    似乎isInstance()调用对于对象定义模块的引用是粘性的,并且希望它以完全相同的表示法导入。(我使用的是2.5版。)

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

    那我该怎么解决这个问题呢?

    细节

    pythonpath:“/”,“/base/”

    文件夹:

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

    基础/材料/foocode.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)
    

    这失败了。它在断言测试中幸存下来,但在初始代码中对断言进行了放大。

    但是,当我修改新的_code以导入barcode.py时,它会起作用:

    import other_stuff.BarCode as bc
    

    . …因为基础/和基础/其他物品都在蟒蛇上。

    4 回复  |  直到 15 年前
        1
  •  2
  •   Denis Otkidach    15 年前

    看起来你有 <root>/ <root>/base 在你 sys.path 这总是很糟糕的。当你这样做的时候 import other_stuff.BarCode as bc 从base/stufacture/foocode.py导入 other_stuff 作为根包,但不是子包 base . 所以做了以后 import base.other_stuff.BarCode as bc 你得到 BarCode 模块导入两次:使用 other_stuff.BarCode base.other_stuff.BarCode .

    最好的解决方案是:

    1. 去除 <根与根基 搜索路径 (或$pythonpath)。
    2. 在base/stufacture/foocode.py中使用相对导入: from ..other_stuff import BarCode as bc .
        2
  •  1
  •   Ferdinand Beyer    15 年前

    您的代码布局严重损坏。您不应该在 sys.path .

    在您的情况下,python将使用两种不同的搜索路径来查找 BarCode.py 因此,将其作为单独模块加载两次, bar.other_stuff.BarCode other_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会处理 bc1 bc2 作为不同的模块。

    确保使用的每个模块都可以通过其完整限定名进行唯一标识,在这种情况下: base.other_stuff.BarCode . 如果模块是包的一部分,则永远不要将包目录添加到 搜索路径 .

        3
  •  1
  •   Alex Martelli    15 年前

    “notation”是问题中最小的一个——定义为语义上引用同一个模块的不同notation保证生成相同的对象。例如。:

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

    相反,问题是,进口同样的产品肯定是有可能的。 文件 不止一次,以不让导入机制完全知道您正在做什么的方式,最终得到不同的模块对象。例如,您正在使用的重叠路径很容易产生这种情况。

    一种方法(如果您坚持以一种潜在的混淆/误导性的方式编写代码和/或布局文件系统;-)是设置 __builtin__.__import__ 调用上一个/普通版本后,检查 __file__ 新导入模块的属性与已导入模块的属性 sys.modules (值得维护这些规则,将文件映射到该文件的规范模块对象) 使用os.path.normpath (或更强大的方法,通过标准库模块中的功能检测单个文件的同义词,如符号链接和硬链接) os )

    使用这个钩子,您可以确保任何一个给定文件的所有导入都将始终产生一个单一的规范模块对象,几乎无论路径和文件系统中发生了什么旋转(对于一个聪明的攻击者来说,仍然可以通过安装他们自己设计的一个棘手的文件系统来屏蔽检查,但是我并不瘦k你实际上是在试图防范蓄意的狡猾攻击;-)。

        4
  •  1
  •   Tendayi Mawushe    15 年前

    你有问题,因为你在sys.path中有基础和其他的东西。

    对于python解释器,有多个条形码模块: bar.other_stuff.BarCode other_stuff.BarCode 第一个位于顶层包中: bar.other_stuff 另一个是独立的顶层包 other_stuff .

    当python解释器搜索sys.path时,它会找到两个完整的不同模块。当您尝试交替使用这两个独立模块中的类时,您会看到错误。

    您需要清理您的python路径,可能只需要将父文件夹放在路径的基础上。