【问题标题】:Why doesn't Python raise an error on namespace collision?为什么 Python 不会在命名空间冲突时引发错误?
【发布时间】:2012-03-17 14:57:38
【问题描述】:

以下 Python 代码正常执行而不会引发异常:

class Foo:
    pass

class Foo:
    pass

def bar():
    pass

def bar():
    pass

print(Foo.__module__ + Foo.__name__)

但很明显,__main__.Foo__main__.bar 有多个实例。为什么 Python 在遇到此命名空间冲突时不会引发错误?而且由于它不会引发错误,它到底在做什么?第一课__main__.Foo被第二课__main__.Foo代替了吗?

【问题讨论】:

  • 给一个变量赋值两次有错吗?
  • 但这不只是分配一个变量吗?它使用每个新的类定义创建新类型。
  • 那又怎样?它正在创建第二个类对象,然后分配名称/“变量”Foo 来引用该类对象,而不是之前引用的对象。模块是一系列语句(大部分类似于函数),class/def 只是语句。

标签: python namespaces python-3.x


【解决方案1】:

在 Python 中,一切都是对象——某种类型的实例。例如。 1int 类型的实例,def foo(): pass 创建对象 foo 这是 function 类型的实例(对于类 - 对象相同,由 class 创建的语句是 type 类型的实例) .鉴于此,两者之间没有区别(在名称绑定机制层面)

class Foo:
  string = "foo1"

class Foo:
  string = "foo2"

a = 1
a = 2

顺便说一句,类定义可以使用type函数(是的,有类型type和内置函数type):

Foo = type('Foo', (), {string: 'foo1'})

因此,类和函数并不是某种不同类型的数据,尽管可以使用特殊语法来创建它们的实例。

另请参阅相关的Data Model 部分。

【讨论】:

    【解决方案2】:

    Foo 类在脚本的下方被有效地重新定义(解释器从上到下读取脚本)。

    class Foo:
      string = "foo1"
    
    class Foo:
      string = "foo2"
    
    f = Foo()
    print f.string
    

    打印“foo2”

    【讨论】:

      【解决方案3】:

      第二个定义替换了第一个定义,如果您将类视为当前命名空间的“类型字典”中的元素,则正如预期的那样:

      >>> class Foo:
      ...     def test1(self):
      ...             print "test1"
      ... 
      >>> Foo
      <class __main__.Foo at 0x7fe8c6943650>
      >>> class Foo:
      ...     def test2(self):
      ...             print "test2"
      ... 
      >>> Foo
      <class __main__.Foo at 0x7fe8c6943590>
      >>> a = Foo()
      >>> a.test1()
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      AttributeError: Foo instance has no attribute 'test1'
      >>> a.test2()
      test2
      >>> 
      

      在这里你可以清楚地看到Foo的“定义”发生了变化(Foo指向内存中的不同类),并且是最后一个占上风的。

      【讨论】:

        【解决方案4】:

        从概念上讲,这只是重新绑定一个名称。和这个没什么区别:

        x = 1
        x = 2
        

        我相信你不希望这是一个错误。

        【讨论】:

          【解决方案5】:

          在编译语言和一些解释语言中,定义、声明和执行之间有明显的区别。但是在python中它更简单。只有陈述!

          Python 执行你的脚本/程序/模块,一旦它被调用。将defclass 视为“语法糖”可能会有所帮助。例如。 class 是 Foo = type("class-name", (bases), {attributes}) 的一个方便的包装器。

          于是python执行:

          class Foo  #equivalent to: Foo = type("class-name", (bases), {attributes})
          class Foo
          def bar
          def bar
          
          print(Foo.__module__ + Foo.__name__)
          

          归结为用最新的“声明”覆盖名称Foobar。所以这只是按照 python-pov 的预期工作 - 但可能不是你想要的! ;-)

          所以这也是不同背景的开发者误会的典型错误:

          def some_method(default_list = []):
              ...
          

          default_list 在这里是一个单例。对some_method 的每次调用都使用相同的default_list,因为列表对象是在第一次执行时创建的。

          Python 不会进入函数体,而只会在开始解析时执行签名/头部。

          【讨论】:

            猜你喜欢
            • 2014-12-16
            • 2017-09-20
            • 2018-03-30
            • 1970-01-01
            • 2010-09-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多