Python: утверждения идентичности объекта, возникающие из-за различий в нотациях операторов импорта - PullRequest
1 голос
/ 30 октября 2009

При проверке идентичности объекта я получаю ошибки утверждения, потому что код создания объекта импортирует модуль определения объекта под одной нотацией (base.other_stuff.BarCode), а код проверки идентичности импортирует тот же модуль под другой нотацией (other_stuff.BarCode). (Пожалуйста, смотрите подробности ниже.)

Кажется, что вызов isinstance () связан с ссылками на модуль определения объекта и хочет импортировать его в точно такой же записи. (Я использую версию 2.5.)

Полагаю, это можно исправить, изменив нотацию импорта в коде, проверяющем идентичность, но я боюсь, что просто распространю ту же проблему на другой код, который зависит от нее. И я уверен, что есть более элегантное решение, которое я, вероятно, должен использовать в первую очередь.

Так как мне это исправить?

ОПИСАНИЕ

PythonPath: '/', '/ base /'

Файлы:

/__init__.py
base/__init__.py
base/other_stuff/__init__.py
base/other_stuff/BarCode.py
base/stuff/__init__.py
camp/__init__.py

Текст базы / stuff / FooCode.py:

import other_stuff.BarCode as bc

class Foo:
    def __init__(self, barThing):
        assert isinstance(barThing, bc.Bar)

Текст лагеря / new_code.py:

import base.stuff.FooCode as fc
import base.other_stuff.BarCode as bc

thisBar = bc.Bar()
assert isinstance(thisBar, bc.Bar)
thisFoo = fc.Foo(barThing=thisBar)

Это не удалось. Он выдерживает проверку утверждения, но взрывается в утверждении в исходном коде.

Однако, это работает, когда я изменяю new_code для импорта BarCode.py с:

import other_stuff.BarCode as bc

. , , потому что и base /, и base / other_stuff находятся в PythonPath.

Ответы [ 4 ]

2 голосов
/ 30 октября 2009

Похоже, у вас есть <root>/ и <root>/base в вашем sys.path, что всегда плохо. Когда вы делаете import other_stuff.BarCode as bc из base / stuff / FooCode.py, он импортирует other_stuff в качестве корневого пакета, но не подпакет base. Итак, после выполнения import base.other_stuff.BarCode as bc вы получите BarCode модуль, импортированный дважды: с other_stuff.BarCode и base.other_stuff.BarCode.

Лучшее решение будет:

  1. Удалить <root>/base из sys.path (или $ PYTHONPATH).
  2. Использовать относительный импорт в base / stuff / FooCode.py: from ..other_stuff import BarCode as bc.
1 голос
/ 30 октября 2009

У вас проблемы, потому что у вас есть и base, и other_stuff в sys.path.

Для интерпретатора Python существует несколько модулей BarCode: bar.other_stuff.BarCode и other_stuff.BarCode первый находится в пакете верхнего уровня: bar.other_stuff, а другой - отдельный пакет верхнего уровня other_stuff.

Когда интерпретатор python ищет sys.path, он находит два полных различных модуля. Когда вы пытаетесь использовать классы из этих двух отдельных модулей взаимозаменяемо, вы получаете ошибки, которые вы видите.

Вам необходимо очистить путь к Python, возможно, поместив только родительскую папку в базу на пути.

1 голос
/ 30 октября 2009

«Нотация» является наименьшей из проблем - разные нотации, определенные для семантической ссылки на один и тот же модуль, гарантированно создают один и тот же объект. E.g.:

>>> import sys as foobar
>>> import sys as zapzip
>>> foobar is zapzip
True

Проблема, скорее, в том, что, несомненно, возможно импортировать один и тот же файл несколько раз, так, чтобы механизм импорта не знал полностью, что вы делаете, и, таким образом, в конечном итоге с различными объектами модуля. Например, перекрывающиеся пути, которые вы используете, могут легко привести к этому.

Один из подходов (если вы настаиваете на написании кода и / или планировании своей файловой системы таким потенциально запутывающим / вводящим в заблуждение способом ;-) - это установить __builtin__.__import__ для вашей собственной функции, которая после вызова предыдущего / нормального версия, проверяет атрибут __file__ вновь импортированного модуля на соответствие атрибутам, уже находящимся в sys.modules (стоит сохранить их, сопоставляя файл с каноническим объектом модуля для этого файла) , используя os.path.normpath (или даже более надежные способы обнаружения синонимов для одного файла, например, символические ссылки и жесткие ссылки, с помощью функциональности в стандартном модуле библиотеки os).

С помощью этого хука вы можете быть уверены, что все операции импорта любого данного файла всегда будут приводить к одному объекту канонического модуля, почти независимо от того, какие переходы происходят в рассматриваемых путях и файловой системе (все еще возможно для умного злоумышленник, чтобы помешать проверкам, установив хитрую файловую систему своего собственного устройства, но я не думаю, что вы на самом деле пытаетесь защититься от преднамеренных хитрых атак; -).

1 голос
/ 30 октября 2009

Ваша раскладка кода серьезно нарушена. У вас не должно быть каталогов пакетов в sys.path.

В вашей ситуации Python будет использовать два разных пути поиска, чтобы найти BarCode.py, поэтому загружая его дважды как отдельные модули, bar.other_stuff.BarCode и other_stuff.BarCode. Это означает, что каждый объект в этом модуле существует дважды, тратя впустую память, и, естественно, идентификация объекта потерпит неудачу:

>>> from base.other_stuff import BarCode as bc1
>>> from other_stuff import BarCode as bc2
>>> bc1
<module 'base.other_stuff.BarCode' from '.../base/other_stuff/BarCode.pyc'>
>>> bc2
<module 'other_stuff.BarCode' from '.../other_stuff/BarCode.pyc'>
>>> bc1 == b2
False
>>> bc1 is bc2
False

Хотя они происходят из одного и того же исходного файла, Python рассматривает bc1 и bc2 как разные модули.

Убедитесь, что каждый используемый вами модуль может быть однозначно идентифицирован по его полному имени, в вашем случае: base.other_stuff.BarCode. Если модуль является частью пакета, никогда не добавляйте каталог пакета в sys.path.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...