Почему Python не вызывает ошибку при столкновении пространства имен? - PullRequest
2 голосов
/ 17 марта 2012

Следующий код 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 ]

6 голосов
/ 17 марта 2012

В Python все является объектом - экземпляром некоторого типа.Например, 1 является экземпляром типа int, 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'})

Так что классы и функции не являются каким-то другим видом данных, хотяспециальный синтаксис может использоваться для создания их экземпляров.

См. также связанный раздел Модель данных .

1 голос
/ 17 марта 2012

В скомпилированных и некоторых интерпретируемых языках существует четкое разделение между определением, объявлением и исполнением.Но в питоне это проще.Есть только операторы!

Python EXECUTES ваш скрипт / программа / модуль, как только он будет вызван.Это может помочь увидеть def и 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__)

, что сводится к перезаписи имен Foo и bar с последним "объявлением",Так что это просто работает так, как задумано от Python-pov - но, возможно, не так, как вы задумали!; -)

, так что это также типичная ошибка для разработчиков с неправильным пониманием фона:

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

default_list здесь является синглтоном.Каждый вызов some_method использует один и тот же default_list, поскольку объект списка создается при первом выполнении.

Python не входит в тело функции, а только выполняет сигнатуру / заголовок, как тольконачинается разбор.

1 голос
/ 17 марта 2012

Класс Foo фактически переопределяется ниже по сценарию (сценарий читается интерпретатором сверху вниз).

class Foo:
  string = "foo1"

class Foo:
  string = "foo2"

f = Foo()
print f.string

печатает "foo2"

1 голос
/ 17 марта 2012

Концептуально это просто повторное связывание имени. Это ничем не отличается от этого:

x = 1
x = 2

и я уверен, что вы не захотите, чтобы это было ошибкой.

1 голос
/ 17 марта 2012

Второе определение заменяет первое, как и ожидалось, если вы рассматриваете классы как элементы в «словаре типов» текущего пространства имен:

>>> 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 указывает на различные классы в памяти), и что это последний, который преобладает.

...