【问题标题】:python namespace: __main__.Class not isinstance of package.Classpython 命名空间:__main__.Class 不是 package.Class 的实例
【发布时间】:2013-03-01 14:17:06
【问题描述】:

假设您有两个如下定义的 python 文件。假设一个是通用包 (class2),另一个执行特定覆盖并用作可执行文件 (class1)。

class1.py:

#!/usr/bin/python
class Test(object):
    pass

class Verificator():
    def check(self, myObject):
        if not isinstance( myObject, Test ):
            print "%s is no instance of %s" % (type(myObject),Test)
        else:
            print "OK!"

if __name__ == '__main__':
    from class2 import getTest

    v = Verificator()
    t = Test()
    v.check(t)
    s = getTest()
    v.check(s)

class2.py:

from class1 import Test
def getTest():
    return Test()

发生的情况是第一次检查正常,而第二次检查失败。原因是t__main__.Testsclass1.Testv.check() 检查__main__.Test,但归根结底是同一类,对吧?

有没有办法编写v.check() 使其也接受class1.Test 对象,或任何其他方式来解决这个问题?

【问题讨论】:

    标签: python namespaces


    【解决方案1】:

    如果您打算从其他地方导入class1.py,请将顶级代码(if __name__ == '__main__': ... 完全移至单独的文件中。这样主文件和class2 使用相同的class1.Test 类。

    几乎做任何其他事情都会打开一罐蠕虫。虽然您可以通过将isinstance 切换到type(myObject).__name__ == ... 来解决直接问题,但事实仍然是您的Python 进程包含两个Test 类,而这些类应该只有一个。否则无法区分的类彼此一无所知,并且无法通过彼此的issubclass 测试。这实际上保证了难以诊断的错误。

    编辑
    另一种选择是在作为 main 执行时从 class1 显式导入类,如您的答案所示。建议更进一步,确保类不是以双精度定义的。例如,您可以将if __name__ == '__main__' 块移动到文件的开头,并以sys.exit(0) 结束:

    if __name__ == '__main__':
        import class1, class2
        ... use only the public API with module prefixes ...
        sys.exit(0)
    
    # the rest of the module follows here
    

    【讨论】:

    • 这可能是一个干净的解决方案,但我实际上想避免将我的场景文件一分为二。
    【解决方案2】:

    感谢您的提示,最终帮助我朝着正确的方向进行实验。我刚刚在这个玩具模型中找到的解决方案是使用导入来修复命名空间问题。为了排除 user4815162342 指出的问题,我在 class1 中添加了另一个类。 class1.py 的以下代码似乎可以执行我想要的操作:

    #!/usr/bin/python
    class Test(object):
        pass
    
    class Toast(object):
        pass
    
    class Verificator():
        def check(self, myObject):
            if not isinstance( myObject, Test ):
                print "NOPE: %s is no instance of %s" % (type(myObject),Test)
            else:
                print "OK: %s is instance of %s" % (type(myObject),Test)
    
    if __name__ == '__main__':
        from class2 import getTest
        from class1 import Test, Toast
    
        v = Verificator()
        t = Test()
        v.check(t)
        t = getTest()
        v.check(t)
        t = Toast()
        v.check(t)
    

    【讨论】:

      【解决方案3】:

      您可以使用type() 函数(实际上,您会发现这种调用相当普遍!):

      #!/usr/bin/python
      class Test(object):
          pass
      
      class Verificator():
          def check(self, myObject):
              if not isinstance( type(myObject), type(Test) ):
                  print "%s is no instance of %s" % (type(myObject),Test)
              else:
                  print "OK!"
      
      if __name__ == '__main__':
          from class2 import getTest
      
          v = Verificator()
          t = Test()
          v.check(t)
          s = getTest()
          v.check(s)
      

      也许更糟糕的解决方案:

      if not isinstance( myObject.__class__, Test.__class__ ):  # Use __class__ here.
      

      它们当然是等价的,但是除非你非常需要,否则需要使用双下划线成员被认为是一种糟糕的形式!不过,值得知道它们的存在,因此我将这个包含在我的答案中。

      请注意,据我所知,这会发生,因为当您运行 python class1.py 时,class1.py 将没有模块。因此,python 会为您将所有内容放入 __main__ 模块中。当您从任何其他脚本导入它时,不是这种情况,因此将某些东西视为 __main__ 模块的一部分实际上是一种特殊情况!

      【讨论】:

      • 我会改用type(myObject)。它归结为同样的事情,但使用的是官方 API,而不是直接进入钩子属性。
      • 检查isinstance(type(myObject), type(Test)) 只测试myObject 的类型是由Test 的元类创建的。这对于由默认元类创建的任何两个新样式类都是如此,并且几乎可以肯定不是 OP 所要求的。
      • 我只在__class__ 上触发。再看一遍,发现只需要testisinstance(myObject, Test)根本不需要查找类型
      • 这个答案是错误的(就像@user4815162342 已经写的一样),它是一个已知的反模式,见:Using type() to compare types
      猜你喜欢
      • 2017-05-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多