为什么Python没有在命名空间冲突时引发错误?

时间:2012-03-17 14:57:39

标签: python namespaces python-3.x

以下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取代?

5 个答案:

答案 0 :(得分:6)

在Python中,一切都是对象 - 某种类型的实例。例如。 1int类型的实例,def foo(): pass创建对象foo,它是类型function的实例(对于类 - 对象,由{{{ 1}}语句是类型class)的实例。鉴于此,

之间没有差异(在名称绑定机制的层面)
type

class Foo:
  string = "foo1"

class Foo:
  string = "foo2"

BTW,可以使用a = 1 a = 2 函数执行类定义(是的,有类型type和内置函数type):

type

所以类和函数不是一些不同类型的数据,尽管可以使用特殊语法来创建它们的实例。

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

答案 1 :(得分:1)

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

>>> 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指向内存中的不同类),并且它是最后一个占优势的。

答案 2 :(得分:1)

Foo类实际上是在脚本中进一步重新定义的(脚本由解释器从上到下读取)。

class Foo:
  string = "foo1"

class Foo:
  string = "foo2"

f = Foo()
print f.string

打印“foo2”

答案 3 :(得分:1)

从概念上讲,这只是重新命名。它与此没有什么不同:

x = 1
x = 2

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

答案 4 :(得分:1)

在编译语言和一些解释语言中,定义,声明和执行之间存在明确的分离。但在python中它更简单。只有声明!

Python EXECUTES 您的脚本/程序/模块一旦被调用。将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,因为list-object是在第一次执行时创建的。

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