【问题标题】:acess class attribute in another file访问另一个文件中的类属性
【发布时间】:2016-07-29 02:21:38
【问题描述】:

我是 python 新手。

我有一个关于在类中访问属性的问题

t1.py

#!/usr/bin/python
import t2

class A:
        flag = False

if __name__ == "__main__":
        t2.f()
        print(A.flag)

t2.py

#!/usr/bin/python
import t1

def f():
        t1.A.flag = True
        print(t1.A.flag)

执行结果:

# ./t1.py
True
False

我希望结果必须是 True,True。

t1.py 中的 A.flag 和 t2.py 中的 t1.A.flag 是否不同?

执行这段代码时python会发生什么?

谢谢。

【问题讨论】:

    标签: python


    【解决方案1】:

    当你这样做时

    ./t1.py
    

    您正在执行t1.py 文件,但它没有作为t1 模块执行。它被认为是__main__ 模块。 (这是if __name__ == '__main__' 行检查的内容。)这意味着当此行:

    import t1
    

    t2.py 尝试导入t1 时,Python 开始执行t1.py 文件再次 以创建t1 模块。您最终会得到两个版本的 A 类,一个是 __main__.A,一个是 t1.A。对t1.A 的修改对__main__.A 没有任何作用,因为即使它们来自同一个文件中的相同代码,它们也不是同一个类。

    【讨论】:

    • 谢谢你的回答。这正是我想要的。
    【解决方案2】:

    它们确实是两个不同的对象(在user2357112's answer 中有很好的解释)。如果你想让t2 使用相同的对象,你需要告诉Python 你实际上导入了与t2 相同的模块。为此,请改为导入 __main__

    import __main__
    
    def f():
        __main__.A.flag = True
        print(__main__.A.flag)
    

    【讨论】:

      【解决方案3】:

      您可以自己找出答案:

      t1.py

      #!/usr/bin/python
      import logging
      logging.basicConfig(level=logging.DEBUG)
      logger = logging.getLogger(__name__)
      logger.debug('t1 has started')
      logger.debug('t2 is being imported')
      import t2
      
      logger.debug('A is being "compiled"')
      class A:
          flag = False
      logger.debug('ID A: %r', id(A))
      logger.debug('ID A.flag %r', id(A.flag))
      
      logger.debug('What is __name__? %r', __name__)
      if __name__ == "__main__":
          logger.debug('__name__ was "__main__"')
          logger.debug('Calling t2.f()')
          t2.f()
          logger.debug('t2.f() was called')
          logger.debug('ID A.flag: %r', id(A.flag))
          print(A.flag)
      

      t2.py

      #!/usr/bin/python
      import logging
      logger = logging.getLogger(__name__)
      logger.debug('t2 is being imported')
      
      logger.debug('t2 is now importing t1')
      import t1
      
      def f():
          logger.debug('f is being called')
          t1.A.flag = True
          logger.debug('ID t1: %r', id(t1))
          logger.debug('ID t1.A: %r', id(t1.A))
          logger.debug('ID t1.A.flag: %r', id(t1.A.flag))
          print(t1.A.flag)
      

      我的输出

      我要和 cmets 分开了

      DEBUG:__main__:t1 has started
      DEBUG:__main__:t2 is being imported
      DEBUG:t2:t2 is being imported
      DEBUG:t2:t2 is now importing t1
      

      如您所见,第一次(正如其他人提到的)t1 实际上的名称为__main__。它尝试导入t2,但立即t2 尝试导入t1

      DEBUG:t1:t1 has started
      DEBUG:t1:t2 is being imported
      

      您可以看到没有任何t2 日志记录语句运行。那是因为 Python 缓存了导入的模块,所以它首先在缓存中查找 t2 并说:“啊哈!我已经导入了这个人,我只需要返回它。那么,给你!”

      DEBUG:t1:A is being "compiled"
      DEBUG:t1:ID A: 140377934341704
      DEBUG:t1:ID A.flag 4312040768
      DEBUG:t1:What is __name__? 't1'
      

      所以,您会注意到现在它已经通过导入t1 实现了。还有t2

      DEBUG:t2:t2 is done being imported
      

      __main__ t1 中继续执行

      DEBUG:__main__:A is being "compiled"
      DEBUG:__main__:ID A: 140377934344360
      DEBUG:__main__:ID A.flag 4312040768
      

      请注意,AA.flagid 是不同的!

      DEBUG:__main__:What is __name__? '__main__'
      DEBUG:__main__:__name__ was "__main__"
      DEBUG:__main__:Calling t2.f()
      DEBUG:t2:f is being called
      DEBUG:t2:ID t1: 4317998840
      DEBUG:t2:ID t1.A: 140377934341704
      DEBUG:t2:ID t1.A.flag: 4312040736
      

      再次注意,这些 ids 匹配 t1.As,而不是 __main__.As。

      True
      DEBUG:__main__:t2.f() was called
      DEBUG:__main__:ID A.flag: 4312040768
      False
      

      【讨论】:

        【解决方案4】:

        是的,我知道这可能有点令人困惑,但这基本上是一个命名空间的问题,以及 __main__ 命名空间不被视为导入模块列表的一部分的区别。这允许作为执行点的文件(因此占用__main__ 命名空间)也可以作为模块导入。另一方面,如果同一个模块被多次导入,解释器将简单地让所有不同的导入指向同一个内存位置

        因此,在您上面显示的代码中,您实际上有两个不同版本的A:您有__main__.A,您有__main__.t2.t1.A。第二个出现是因为__main__正在导入t2,而t1又将t1作为模块导入。

        当您运行t2.f() 时,您正在设置__main__.t2.t1.A.flag = True 然后打印它。随后,当您调用print(A.flag) 时,您将打印__main__.A.flag 中的值,该值从未更改过。

        我希望这至少有点道理。

        我的一个朋友总是说计算机科学是一门实验科学。 让我们调用调试器。

        我在执行中添加了pdb.set_trace()t1.py 现在看起来像这样:

        #!/usr/bin/python
        import t2
        
        class A:
                flag = False
        
        if __name__ == "__main__":
            import pdb; pdb.set_trace()
            t2.f()
            print(A.flag)
        

        这就是我们得到的:

        $ python t1.py
        > /Users/martin/git/temp/t1.py(9)<module>()
        -> t2.f()
        (Pdb) A
        <class __main__.A at 0x10ec9ba78>
        (Pdb) t2.t1.A
        <class t1.A at 0x10ec9ba10>
        (Pdb) 
        

        请注意,A 具有关联的单独内存位置。

        【讨论】:

        • 不涉及__main__ 的循环导入不会触发此行为。如果循环导入总是导致重新执行重新导入的模块,那么由于无限递归导入,您会遇到堆栈溢出。 __main__ 在这里很重要。
        • 没错,在这方面它确实以不同的方式对待__main__。我会相应地调整我的答案。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-04-09
        • 2021-11-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-02-18
        • 2019-06-05
        相关资源
        最近更新 更多