2013-11-15 3 views
8

user.py :제거 파이썬 원형 수입

from story import Story 

class User: 
    ... 
    def get_stories(self): 
     story_ids = [select from database] 
     return [Story.get_by_id(id) for id in story_ids] 

story.py

from user import User 

class Story: 
    ... 
    def __init__(self, id, user_id, content): 
     self.id = id 
     self.user = User.get_by_id(user_id) 
     self.content = content 

당신이 볼 수는 ImportError 원인이 프로그램에 원형의 수입이있다. 이 오류를 방지하기 위해 import 정의문을 import 정의문으로 옮길 수 있다는 것을 알게되었습니다. 하지만 여전히 알고 싶습니다.이 경우 순환 수입을 제거 할 수있는 방법이 있습니까? 아니면 좋은 디자인을 위해 필요한 것입니까?

+0

좋은 디자인을 위해 원형 가져 오기를 제거 할 필요는 없습니다. 가져 오기를 메소드 정의로 이동하면 가져 오기를 연기하는 것이 합리적입니다. –

답변

1

원형을 완화하는 또 다른 방법은 가져 오기 스타일을 변경하는 것입니다. from story import Storyimport story으로 변경 한 다음 클래스를 story.Story으로 지정하십시오. 메소드 내에서 클래스를 참조하기 만하기 때문에 메소드가 호출 될 때까지 클래스에 액세스 할 필요가 없으며 가져 오기가 성공적으로 완료됩니다. (먼저 가져 오는 모듈에 따라이 모듈 중 하나 또는 두 모듈을 모두 변경해야 할 수 있습니다.)

그러나 디자인이 다소 이상하게 보입니다. 귀하의 디자인은 UserStory 클래스가 매우 밀접하게 결합되어 있으므로 다른 클래스없이 사용할 수 있습니다. 그러한 경우, 일반적으로 두 모듈을 동일한 모듈에 둘 필요가 있습니다.

+0

그래, 나는이 디자인이 다소 이상하다고 생각한다 ... 이것은 일반적인 상황이기 때문에 나는 좋은 디자인이 어떻게 생겼는지 궁금하다. 덕분에 – wong2

+1

글쎄,이 __isn't__ 일반적인 상황 :) –

+0

제안 된 변경 사항은 문제를 해결하지 않을 것입니다, 그것은 여전히 ​​원형 가져 오기가있을 것입니다. –

0

BrenBarn이 말했듯이 가장 확실한 해결책은 사용자와 스토리를 동일한 모듈에 보관하는 것이므로 사용자가 스토리에 대해 알기로되어있는 경우에 완벽하게 이해할 수 있습니다. 이제 인 경우 별개의 모듈에 추가해야합니다. 사용자는 메서드를 추가하기 위해 story.py에서 monkeypatch 할 수도 있습니다. 그것은이 경우

1

가장 확실한 해결책은 Story 생성자는 실제 User 아닌 user_id 받아들이도록 인터페이스를 변경하여, 완전히 User 클래스에 의존성을 파괴하는 것입니다 ... 오프 가독성/감 무역입니다. 이것은 또한보다 효율적인 설계로 이끈다. 예를 들어, 사용자가 많은 이야기를 가지고 있다면, 같은 객체가 모든 생성자에게 주어질 수있다.

그 외의 경우 전체 모듈 가져 오기 (구성원 대신 storyuser)가 작동해야합니다. 가져온 모듈은 두 번째 가져 오기시 비어있는 것으로 나타납니다. 그러나 이러한 모듈의 내용이 전역 범위에서 사용되지 않기 때문에 중요하지 않습니다.

메서드 내에서 가져 오는 것보다 약간 바람직합니다. 메소드 내에서 가져 오기는 각 메소드 호출마다 완료되어야하기 때문에 모듈 - 글로벌 조회 (story.Story)에 비해 상당한 오버 헤드가 있습니다. 단순한 경우 오버 헤드가 최소한 30 배가되는 것 같습니다.

1

웹상에 이러한 python 원형 질문이 여러 개 있습니다. Ray Hettinger가 순환 가져 오기의 유즈 케이스를 합법화하는 주석을 가지고 있기 때문에이 스레드에 기여하기로 결정했지만 가져 오기를 메소드로 이동하는 특히 좋은 연습은 아니라고 생각하는 솔루션을 권장합니다. 그렇다 Hettinger의 기관에서

는 일반적인 반대 세 면책 조항이 필요하다 :

  1. 는 내가 자바로 프로그램 된 적이 없습니다. Java 스타일을 시도하지 않습니다.
  2. 리팩토링이 항상 유용하거나 효과적인 것은 아닙니다.논리적 API는 때때로 반복적 인 import 참조를 피할 수 없도록 만드는 구조를 지시합니다. 프로그래머가 아닌 사용자를위한 코드가 있다는 것을 기억하십시오.
  3. 상당히 큰 모듈을 결합하면 가독성 및 유지 관리 문제가 발생할 수 있습니다.이 문제는 하나 또는 두 개의 재귀 수입보다 훨씬 나쁠 수 있습니다. 또한

, 나는, 유지 보수성 및 가독성이 수입이 파일의 상단에 분류 할 것을 지시 생각 각각의 필요에 이름을 한 번만 발생하고, from module import name 스타일은 매우 짧은 모듈 이름을 아마 제외 (바람직하다 반복적 인 언어 적 혼란을 피하고 종속성을 명시 적으로 만들어주기 때문에 많은 기능 (예 : gtk)이 필요합니다.

그런 식으로 나에게 가져온 내 자신의 유스 케이스의 단순화 된 버전을 제시하고 해결책을 제시 할 것입니다.

나는 두 개의 모듈을 가지고 있는데, 각각은 많은 클래스를 정의한다. surface은 평면, 구, 쌍곡면과 같은 기하학적 인면을 정의합니다. path은 선, 원의 쌍곡선 등과 같은 평면 기하학적 수치를 논리적으로 정의합니다. 이들은 논리적으로 별개의 범주이며 API 요구 사항의 관점에서 볼 때 리팩토링은 옵션이 아닙니다. 그럼에도 불구하고이 두 범주는 친밀합니다.

유용한 작업은 두 개의면을 교차시킵니다. 예를 들어 두면의 교차가 선이거나 평면과 구의 교차가 원입니다. 예를 들어, surface.py에 교차로 작업에 대한 반환 값을 구현하는 데 필요한 똑바로 앞으로 수입 할 경우

:

from path import Line 

를 당신이 얻을 :

Traceback (most recent call last): 
    File "surface.py", line 62, in <module> 
    from path import Line 
    File ".../path.py", line 25, in <module> 
    from surface import Plane 
    File ".../surface.py", line 62, in <module> 
    from path import Line 
ImportError: cannot import name Line 

을 기하학적으로, 비행기는 데 사용됩니다 결국 경로를 정의하면 3 가지 (또는 그 이상) 차원에서 임의로 방향을 지정할 수 있습니다. 추적 표시는 현재 일어나고있는 일과 해결 방법을 알려줍니다. 추적이 다시 여전히 일어나고에

try: from path import Line 
except ImportError: pass # skip circular import second pass 

작업의 순서 :

는 간단히과 surface.py에 import 문을 교체합니다. 두 번째로 가져 오기 실패를 무시합니다. 이는 모듈 수준에서 Line이 사용되지 않았으므로 중요하지 않습니다. 따라서 필요한 네임 스페이스 surfacepath에로드됩니다. 따라서 path의 네임 스페이스 구문 분석을 완료하여 surface에로드하고 from path import Line으로 첫 번째 만남을 완료 할 수 있습니다. 따라서 surface의 네임 스페이스 구문 분석은 계속 진행될 수 있으며 필요한 다른 작업을 계속할 수 있습니다.

쉽고 명확한 관용어입니다. try: ... except ... 구문은 순환 수입 문제를 명확하고 간결하게 문서화하여 향후 유지 보수가 필요할 때마다 완화합니다. 리펙터가 실제로 나쁜 생각 일 때마다 사용하십시오.