Python: невозможно импортировать два взаимозависимых класса, которые содержат только статические методы [duplicate]

Использование блестящей идеи DSM:

from __future__ import print_function

import pandas as pd

categories = set(['Economics', 'English', 'Histo', 'Literature'])

def correct_categories(cols):
    return [cat for col in cols for cat in categories if col.startswith(cat)]    

df = pd.read_csv('data.csv', sep=r'\s+', index_col='Id')

#print(df)
print(df.groupby(correct_categories(df.columns),axis=1).sum())

Выход:

    Economics  English  Histo  Literature
Id
56          1        1      2           1
11          1        0      0           1
6           1        1      0           0
43          2        0      1           1
14          1        1      1           0

Вот еще одна версия, которая заботится о «Histo / History» проблематичной ..

from __future__ import print_function

import pandas as pd

#categories = set(['Economics', 'English', 'Histo', 'Literature'])

#
# mapping: common starting pattern: desired name
#
categories = {
    'Histo': 'History',
    'Economics': 'Economics',
    'English': 'English',
    'Literature': 'Literature'
}

def correct_categories(cols):
    return [categories[cat] for col in cols for cat in categories.keys() if col.startswith(cat)]

df = pd.read_csv('data.csv', sep=r'\s+', index_col='Id')
#print(df.columns, len(df.columns))
#print(correct_categories(df.columns), len(correct_categories(df.columns)))
#print(df.groupby(pd.Index(correct_categories(df.columns)),axis=1).sum())

rslt = df.groupby(correct_categories(df.columns),axis=1).sum()
print(rslt)
print('History\n', rslt['History'])

Выход:

    Economics  English  History  Literature
Id
56          1        1        2           1
11          1        0        0           1
6           1        1        0           0
43          2        0        1           1
14          1        1        1           0
History
 Id
56    2
11    0
6     0
43    1
14    1
Name: History, dtype: int64

PS Вы можете захотеть добавить отсутствующие категории в categories map / dictionary

267
задан NullUserException 6 October 2012 в 20:07
поделиться

11 ответов

В прошлом году на comp.lang.python было очень хорошее обсуждение этого вопроса. Он очень хорошо отвечает на ваш вопрос.

Импорт довольно прост. Просто помните следующее:

'import' и 'from xxx import yyy' являются исполняемыми операциями. Они выполняются, когда запущенная программа достигает этой строки.

Если модуль отсутствует в sys.modules, тогда импорт создает новую запись модуля в sys.modules и затем выполняет код в модуле. Он не возвращает управление вызывающему модулю до тех пор, пока выполнение не завершится.

Если модуль существует в sys.modules, тогда импорт просто возвращает этот модуль, завершил ли он выполнение. Вот почему циклический импорт может возвращать модули, которые кажутся частично пустыми.

Наконец, исполняемый скрипт работает в модуле с именем __main__, при импорте скрипта под собственным именем будет создан новый модуль, не связанный с __main __.

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

230
ответ дан Martijn Pieters 20 August 2018 в 11:13
поделиться
  • 1
    NB: семантика семантического импорта изменилась в Python3! – meawoppl 29 January 2016 в 20:02
  • 2
    @meawoppl Не могли бы вы расширить этот комментарий, пожалуйста? Как конкретно они изменились? – Dan Schien 7 April 2016 в 10:17
  • 3
    На данный момент единственная ссылка на круговой импорт в python3 «Что нового?» страниц в 3.5 одном . В нем говорится: «Циркулярный импорт, связанный с относительным импортом, теперь поддерживается». @meawoppl вы нашли что-нибудь еще, что не указано на этих страницах? – zezollo 21 April 2016 в 05:38
  • 4
    Они деф. не поддерживается в 3.0-3.4. Или, по крайней мере, семантика успеха отличается. Вот краткий обзор, который я нашел, что не упоминает 3.5 изменений. gist.github.com/datagrok/40bf84d5870c41a77dc6 – meawoppl 22 April 2016 в 19:02

Хорошо, я думаю, что у меня довольно крутое решение. Допустим, у вас есть файл a и файл b. У вас есть def или class в файле b, который вы хотите использовать в модуле a, но у вас есть что-то еще, либо def, class, либо переменная из файла a что вам нужно в вашем определении или классе в файле b. Что вы можете сделать, это внизу файла a после вызова функции или класса в файле a, которая необходима в файле b, но прежде чем вызывать функцию или класс из файла b, который вам нужен для файла a, скажем import b Затем, и вот ключевая часть во всех определениях или классах файла b, которым нужны def или class из файла a (назовем это CLASS), вы говорите from a import CLASS

Это работает, потому что вы можете импортировать файл b без Python, выполняющего какие-либо из операторов импорта в файле b, и, таким образом, вы избегаете любых циклических импорта.

Например:

Файл a:

class A(object):

     def __init__(self, name):

         self.name = name

CLASS = A("me")

import b

go = B(6)

go.dostuff

Файл b:

class B(object):

     def __init__(self, number):

         self.number = number

     def dostuff(self):

         from a import CLASS

         print "Hello " + CLASS.name + ", " + str(number) + " is an interesting number."

Voila.

-2
ответ дан das-g 20 August 2018 в 11:13
поделиться
  • 1
    from a import CLASS фактически не пропускает выполнение всего кода в a.py. Я думаю, это то, что на самом деле происходит здесь: – Matthias Fripp 4 June 2015 в 07:17
  • 2
    from a import CLASS фактически не пропускает выполнение всего кода в a.py. Это то, что на самом деле происходит: (1) Весь код в a.py запускается как специальный модуль «__ main __». (2) В import b код верхнего уровня в b.py запускается (определяя класс B), а затем управляет возвратом в «__ main __». (3) "__ main __" в конечном итоге передает управление go.dostuff(). (4), когда dostuff () приходит к import a, он запускает весь код в a.py снова , на этот раз в качестве модуля «a»; затем он импортирует объект CLASS из нового модуля «a». На самом деле, это будет работать одинаково хорошо, если вы использовали import a в любом месте b.py. – Matthias Fripp 4 June 2015 в 07:28

Циркулярный импорт может сбивать с толку, потому что импорт делает две вещи:

  1. он выполняет импортированный код модуля
  2. добавляет импортируемый модуль для импорта глобальной таблицы символов модуля

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

Циркулярный импорт не является наивысшим злом, которого можно избежать любой ценой. В некоторых рамках, таких как Flask, они вполне естественны, и настройка вашего кода для их устранения не делает код лучше.

main.py

print 'import b'
import b
print 'a in globals() {}'.format('a' in globals())
print 'import a'
import a
print 'a in globals() {}'.format('a' in globals())
if __name__ == '__main__':
    print 'imports done'
    print 'b has y {}, a is b.a {}'.format(hasattr(b, 'y'), a is b.a)

b.by

print "b in, __name__ = {}".format(__name__)
x = 3
print 'b imports a'
import a
y = 5
print "b out"

a.py

print 'a in, __name__ = {}'.format(__name__)
print 'a imports b'
import b
print 'b has x {}'.format(hasattr(b, 'x'))
print 'b has y {}'.format(hasattr(b, 'y'))
print "a out"

python main.py вывод с комментариями

import b
b in, __name__ = b    # b code execution started
b imports a
a in, __name__ = a    # a code execution started
a imports b           # b code execution is already in progress
b has x True
b has y False         # b defines y after a import,
a out
b out
a in globals() False  # import only adds a to main global symbol table 
import a
a in globals() True
imports done
b has y True, a is b.a True # all b objects are available
0
ответ дан Jacek Błocki 20 August 2018 в 11:13
поделиться

У меня есть пример, который поразил меня!

foo.py

import bar

class gX(object):
    g = 10

bar.py

from foo import gX

o = gX()

main.py

import foo
import bar

print "all done"

В командной строке: $ python main.py

Traceback (most recent call last):
  File "m.py", line 1, in <module>
    import foo
  File "/home/xolve/foo.py", line 1, in <module>
    import bar
  File "/home/xolve/bar.py", line 1, in <module>
    from foo import gX
ImportError: cannot import name gX
8
ответ дан Mohith 20 August 2018 в 11:13
поделиться
  • 1
    Как вы это исправили? Я пытаюсь понять круговой импорт, чтобы исправить собственную проблему, которая выглядит как very , аналогичную тому, что вы делаете ... – c089 9 August 2010 в 07:53
  • 2
    Errm ... Кажется, я исправил свою проблему с этим невероятно уродливым взломом. {{{if not 'foo.bar' в sys.modules: from foo import bar else: bar = sys.modules ['foo.bar']}}} Лично я считаю, что круговой импорт является ОГРОМНЫМ предупреждающим знаком о плохом коде дизайн... – c089 9 August 2010 в 08:01
  • 3
    @ c089, или вы можете просто переместить import bar в foo.py до конца – warvariuc 5 August 2013 в 10:52
  • 4
    Если bar и foo оба должны использовать gX, «самое чистое» решение - поместить gX в другой модуль и импортировать этот модуль foo и bar. (самый чистый в том смысле, что нет скрытых семантических зависимостей.) – Tim Wilder 17 December 2013 в 22:32
  • 5
    У Тима хорошая точка. В основном это потому, что bar не может даже найти gX в foo. круговой импорт сам по себе хорош, но это просто, что gX не определяется при импорте. – Martian2049 18 January 2017 в 04:31

Модуль a.py:

import b
print("This is from module a")

Модуль b.py

import a
print("This is from module b")

Запустит «Модуль a»:

>>> 
'This is from module a'
'This is from module b'
'This is from module a'
>>> 

Он выводит эти 3 строки, пока он должен выводить бесконечность из-за циклического импорта. Что происходит строка за строкой при запуске «Module a»:

  1. Первая строка - import b. поэтому он посетит модуль b
  2. . Первая строка в модуле b - import a. поэтому он будет посещать модуль a
  3. . Первая строка в модуле a - import b, но отмечает, что эта строка больше не будет выполняться больше , поскольку каждый файл на python выполняет импортировать строку только один раз, не имеет значения, где и когда он выполняется. поэтому он перейдет к следующей строке и напечатает "This is from module a".
  4. После завершения посещения всего модуля a из модуля b мы все еще находимся в модуле b. поэтому следующая строка будет печатать "This is from module b"
  5. . Строки модуля b выполняются полностью. поэтому мы вернемся к модулю a, где мы запустили модуль b.
  6. строка импорта b была уже выполнена и больше не будет выполнена. следующая строка будет печатать "This is from module a", и программа будет закончена.
0
ответ дан Mohsen Haddadi 20 August 2018 в 11:13
поделиться

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

# Hack to import something without circular import issue
def load_module(name):
    """Load module using imp.find_module"""
    names = name.split(".")
    path = None
    for name in names:
        f, path, info = imp.find_module(name, path)
        path = [path]
    return imp.load_module(name, f, path[0], info)
constants = load_module("app.constants")

Опять же, это не постоянное исправление, но может помочь кому-то, кто хочет исправить ошибку импорта без меняя слишком много кода.

Приветствия!

3
ответ дан radtek 20 August 2018 в 11:13
поделиться

Как и другие ответы, этот шаблон допустим в python:

def dostuff(self):
     from foo import bar
     ...

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

Большинство Circular Imports на самом деле не являются логическим циклическим импортом, а скорее повышают ошибки ImportError из-за того, как import() оценивает операторы верхнего уровня весь файл при вызове.

Эти ImportErrors можно почти всегда избегать, если вы действительно хотите, чтобы ваш импорт был сверху:

Рассмотрим этот циклический импорт:

App A

# profiles/serializers.py

from images.serializers import SimplifiedImageSerializer

class SimplifiedProfileSerializer(serializers.Serializer):
    name = serializers.CharField()

class ProfileSerializer(SimplifiedProfileSerializer):
    recent_images = SimplifiedImageSerializer(many=True)

Приложение B

# images/serializers.py

from profiles.serializers import SimplifiedProfileSerializer

class SimplifiedImageSerializer(serializers.Serializer):
    title = serializers.CharField()

class ImageSerializer(SimplifiedImageSerializer):
    profile = SimplifiedProfileSerializer()

От David Beazleys отличный разговор Модули и пакеты: Live и Let Die! - PyCon 2015 , 1:54:00, вот способ обработки круговых импортов в python:

try:
    from images.serializers import SimplifiedImageSerializer
except ImportError:
    import sys
    SimplifiedImageSerializer = sys.modules[__package__ + '.SimplifiedImageSerializer']

Это попытка импортировать SimplifiedImageSerializer, и если ImportError поднят, потому что он уже импортируется, он вытащит его из importcache.

PS: Вы должны прочитать весь этот пост в голосе Дэвида Бэйсли.

22
ответ дан Sebastian Wozny 20 August 2018 в 11:13
поделиться
  • 1
    ImportError не возникает, если модуль уже импортирован. Модули могут быть импортированы столько раз, сколько вы хотите, чтобы «импортировать»; import a; & quot; это нормально. – Yuras 5 May 2016 в 08:39

Циклический импорт завершается, но вам нужно быть осторожным, чтобы не использовать циклически импортированные модули во время инициализации модуля.

Рассмотрим следующие файлы:

a.py:

print "a in"
import sys
print "b imported: %s" % ("b" in sys.modules, )
import b
print "a out"

b.py:

print "b in"
import a
print "b out"
x = 3

Если вы выполните a.py, вы получите следующее:

$ python a.py
a in
b imported: False
b in
a in
b imported: True
a out
b out
a out

Во втором импортировании b.py (во втором a in) интерпретатор Python не импортирует b еще раз, потому что он уже существует в модуле dict.

Если вы попытаетесь получить доступ к b.x из a во время инициализации модуля вы получите AttributeError.

Добавьте следующую строку к a.py:

print b.x

Затем на выходе:

$ python a.py
a in                    
b imported: False
b in
a in
b imported: True
a out
Traceback (most recent call last):
  File "a.py", line 4, in <module>
    import b
  File "/home/shlomme/tmp/x/b.py", line 2, in <module>
    import a
 File "/home/shlomme/tmp/x/a.py", line 7, in <module>
    print b.x
AttributeError: 'module' object has no attribute 'x'

Это связано с тем, что модули выполняются при импорте и в момент доступа к b.x линия x = 3 еще не выполнена, что произойдет только после b out.

91
ответ дан the Tin Man 20 August 2018 в 11:13
поделиться
  • 1
    это очень объясняет проблему, но как насчет решения? как мы могли правильно импортировать и печатать x? другое решение выше не работало для меня – mehmet 27 March 2018 в 18:23

Если вы выполняете import foo внутри bar и import bar внутри foo, он будет работать нормально. К тому моменту, когда что-то действительно выполняется, оба модуля будут полностью загружены и будут иметь ссылки друг на друга.

Проблема заключается в том, что вместо этого вы делаете from foo import abc и from bar import xyz. Поскольку теперь каждый модуль требует, чтобы другой модуль уже был импортирован (чтобы имя, которое мы импортируем, существует), прежде чем оно может быть импортировано.

215
ответ дан user2357112 20 August 2018 в 11:13
поделиться
  • 1
    Похоже, что from foo import * и from bar import * также будут работать нормально. – Akavall 12 May 2014 в 17:54
  • 2
    Проверьте редактирование сообщения выше, используя команду a.py/b.py. Он не использует from x import y и все еще получает круговую ошибку импорта – Greg Ennis 30 June 2014 в 15:09
  • 3
    Это не совсем правда. Точно так же, как import * from, если вы попытаетесь получить доступ к элементу в циклическом импорте на верхнем уровне, чтобы до завершения скрипта выполнить его, тогда у вас будет такая же проблема. Например, если вы устанавливаете глобальный пакет в одном пакете из другого, и оба они включают друг друга. Я делал это, чтобы создать неаккуратную фабрику для объекта в базовом классе, где этот объект может быть одним из нескольких подклассов, а используемый код не должен был знать о том, что он на самом деле создает. – AaronM 13 April 2016 в 20:54
  • 4
    @Akavall Не совсем. Это приведет только к импорту имен, доступных при выполнении оператора import. Таким образом, это не будет ошибкой, но вы не можете получить все переменные, которые вы ожидаете. – augurar 24 December 2016 в 02:41
  • 5
    Обратите внимание, что если вы выполняете from foo import * и from bar import *, все, что выполняется в foo, находится в фазе инициализации bar, а фактические функции в bar еще не определены ... – Martian2049 18 January 2017 в 03:37
1
ответ дан Irfanuddin Shafi 31 October 2018 в 09:13
поделиться
0
ответ дан Usman Hussain 31 October 2018 в 09:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: