2016-08-03 2 views
0

저는 XLS/XLSX/CSV 스프레드 시트와 상호 작용하는 데 사용되는 파이썬 스크립트 작업을하고 있습니다.파이썬에서 3 단계 깊이로 클래스를 중첩하는 것은 나쁜 습관입니까?

  1. Workbook 분류 : 서로에 중첩되는 세 가지 주요 클래스

    세 기본 클래스

    아래 설명된다 (서로 연장 하지는 클래스가 다른 클래스 내에 그대로이다)이있다 이것은 XLS/XLSX/CSV 클래스의 팩토리 메서드입니다. 외부 액세스 가능
  2. 파일 내에서 특정 스프레드 시트 나 워크 시트를 여는 데 사용되는 Workbook 클래스의 __Worksheet 클래스 전용 클래스입니다. 이것은 Workbook.worksheet() 방법을 통해서만 접근 가능합니다
  3. __Worksheet 클래스의 개인 __Cell 클래스는 셀 자체와 상호 작용합니다. 이것은 외부 __Worksheet 클래스 Heres는

지금까지 클래스 구조의 단순화 된 버전을 통해 오히려에서만 액세스 할 수 액세스 할 수 없게해야합니다

class Workbook(object): 

    def __init__(self, file_name): 
     self.__file_name = file_name 

    def worksheet(self, name): 
     return self.__Worksheet(self, name) 

    class __Worksheet(): 
     def __init__(self, workbook, worksheet): 
      self.__workbook = workbook 

     def cell(self, cell_id, data = None): 
      return self.__Cell(cell_id, data) 

     class __Cell(): 
      def __init__(self, cell, data = None): 
       self.__cell = cell 
       self.__data = data 

      def setVal(self, data): 
       self.__data = data 

      def __str__(self): 
       return self.__data 

workbook = Workbook('test-file.csv') 
worksheet = workbook.worksheet('First Worksheet') 
cell_A1 = worksheet.cell('A1', 'Foo...') 

print("Before - Cell A1: %s" % cell_A1) # => Before - Cell A1: Foo... 
cell_A1.setVal('Bar...') 
print("After - Cell A1: %s" % cell_A1) # => Before - After - Cell A1: Bar... 

그래서 내가 가지고있는 질문은 - 나쁜 생각인가 수업 시간에 수업을 듣는 연습을하십니까?

저는 약간 파이썬에 익숙하지 않지만, 제 경험은 대부분 PHP/JS/Perl입니다. 파이썬에서 클래스 내에서 클래스를 가지는 것은 너무 드문 것처럼 보이지만, 어떤 이유로 클래스 3 레벨을 중첩하는 것은 잘못된 것처럼 보입니다. 그것이 있다면, 그리고 그것을 할 수있는 더 좋은 방법이 있다면, 그것은 좋을 것입니다.

나는 대안 하지 중첩 클래스 알고, 그리고 Workbook의 인스턴스가 매개 변수로 Worksheet에 주어진다면 바로 확인. 그런 다음 Worksheet 인 경우 인스턴스를 반환하는 에 메소드를 생성하고 시작하는 데 사용되는 매개 변수 중 하나로 self을 전달합니다.

예 :

class Workbook(object): 
    def __init__(self, file_name): 
     self.__file_name = file_name 

    def worksheet(self, name): 
     return self.Worksheet(self, name) 

class Worksheet(object): 
    def __init__(self, workbook, worksheet = 0): 
     if not isinstance(workbook, Workbook): 
      raise Exception('Expected the workbook to be an instance of the Workbook class') 

     self.__workbook = workbook 

    def cell(self, cell_id, data = None): 
     return self.Cell(cell_id, data) 

class Cell(object): 
    def __init__(self, worksheet, cell, data = None): 
     if not isinstance(worksheet, Worksheet): 
      raise Exception('Expected the worksheet to be an instance of the Worksheet class') 

     self.__cell = cell 
     self.__data = data 

    def setVal(self, data): 
     self.__data = data 

    def __str__(self): 
     return self.__data 

# Example Usage One 
workbook = Workbook('test-file.xls') 
worksheet = workbook.worksheet('First Worksheet') 
cell_A1 = worksheet.cell('A1', 'Foo...') 

print("Before - Cell A1: %s" % cell_A1) # => Before - Cell A1: Foo... 
cell_A1.setVal('Bar...') 
print("After - Cell A1: %s" % cell_A1) # => Before - After - Cell A1: Bar... 

# Example Usage Two 
workbook = Workbook('test-file.xlsx') 
worksheet = Worksheet(workbook, 'First Worksheet') 
cell_A1 = Cell(worksheet, 'A1', 'Foo...') 

print("Before - Cell A1: %s" % cell_A1) # => Before - Cell A1: Foo... 
cell_A1.setVal('Bar...') 
print("After - Cell A1: %s" % cell_A1) # => Before - After - Cell A1: Bar... 

# Failed Example 
worksheet = Worksheet('Not worksheet', 1) # => Exception, as expected 

그러나이 대안은 WorksheetCell 클래스 외부에서 액세스 할 수 있으며 수동으로 시작할 수 있습니다 ...하지만 내가하지 끔찍한 일 그게 전부 추측 것을 의미한다.

가장 좋은 방법은 무엇이라고 생각하는지 알려주세요. 이 게시물에 댓글 하나는 another SO post에 대한 링크를 제공하는 첫 번째있는 사용자 게시물 중첩 클래스의 3 장점 :

클래스의 논리적 그룹 : 클래스는 하나의 유용한 경우 다른 클래스의 경우 클래스에 해당 클래스를 포함시키고 두 클래스를 함께 유지하는 것이 논리적입니다. 이러한 "헬퍼 클래스"를 중첩하면 패키지가보다 간결 해집니다.

내가 정확히 생각한 것입니다. 나는 단지 2를하기 전에 그것들을 3 개의 레이어에 중첩시키는 것이 다소 어색하다고 생각했습니다.

+0

"다음과 같은 행을 포함 시키면 충분합니다. 그러면 더 좋은 방법이 될 것입니다."- 그렇지 않습니까? – BartoszKP

+2

왜 그랬겠습니까? 모듈 레벨을 모두 모듈화하는 것과 비교하면 어떤 이점도 얻지 못합니다. – user2357112

+0

[나는 나쁜 행동이라고 생각하지 않는다] (http://stackoverflow.com/questions/719705/what-is-the-purpose-of-pythons-inner-classes). –

답변

3

질문하기 : 클래스를 중첩하여 작성하면 어떤 장점이 있습니까?함수와 달리 클래스는 "어휘 범위"(, 즉과 다르며 현재 클래스의 네임 스페이스에서 확인할 수없는 이름은 주변 클래스에서 해결되지 않습니다)를 사용하지 않습니다. 따라서 Workbook 인스턴스와 관련된 __Worksheet 클래스를 참조해야합니다.

이것은 다시 말해서 사용자가 사용한 중첩에는 인식할만한 이점이 없다는 것을 의미합니다. 경험 많은 파이썬 프로그래머는 중첩을 사용하지 않고 예제를 작성합니다. 이렇게하면 코드가 단순 해집니다 (클래스 이름이 모두 포함 된 모듈에 대해 전역이므로).

이것은 함수 본문 내부에서 클래스를 선언하는 것과 매우 다릅니다. 이 경우 클래스의 코드는 함수의 네임 스페이스에있는 변수를 참조 할 수 있으며 Python은 함수 호출이 종료되고 호출과 관련된 로컬 네임 스페이스가있는 경우에도 해당 참조가 클래스에서 계속 사용 가능하도록 보장합니다. 파괴되었습니다.

+0

업데이트 된 게시물의 마지막 부분에서 대체 방법을 사용 하시겠습니까? – Justin

+0

@ Justin 질문의 맨 아래에 링크 된 답변은 끔찍합니다. 나는 이걸 고집 할 것이다. – BartoszKP

0

다른 수업에서 제기 된 질문 중 일부를 제외하고 나는이 수업을 모두 중첩하지 않는 것이 좋습니다. 그것들이 어떻게 사용되도록 의도되었는지 간단하게 문서화하는 것이 낫습니다. 예를 들어 WorksheetReference 및 CellReference와 같이 클래스 자체의 매력을 떨어 뜨리는 대체 이름을 고려하십시오. 단지 다음과 같은 것을 말하는 경우에도 몇 개의 문서 문자열에 던져 넣으십시오 :

WorksheetReference는 직접 인스턴스화하는 것이 아닙니다. 통합 문서의 worksheet() 메서드를 통해 액세스해야합니다.

나머지 답변은 이유를 살펴 봅니다.

중첩의 비용과 이점은 무엇입니까?

사례에 클래스를 중첩하면 추가 중첩 만 제공됩니다. 따라서 중첩 된 클래스에 대해 이야기하기가 더 어려워집니다. Worksheet이 아니며 Workbook.__Worksheet입니다. (실제로는 Workbook._Workbook__Worksheet이지만 더 말할 것도 없습니다.) 이야기하기가 더 어렵 기 때문에 인터페이스를 문서화하는 것이 더 어렵습니다. 누군가 myworkbook.worksheet으로 전화를 통해 돌아 오는 것을 사용하는 방법을 알고 있습니다. 귀하의 문서가 "세포 방법으로 개체를 반환"또는 "통합 문서를 반환합니다 .__ 워크 시트 개체"또는 완전히 다른 접근 방식을 취할 것이라고?

이 중첩 기능을 사용하면 누군가가 클래스를 인스턴스화하지 못하게 할 수 있습니다. 따라서 "오용"을 방지하는 데 진정으로 관심이 있다면 워크 시트의 __init__ 함수에 전달 된 값을 확인해야합니다 (자세한 내용은 나중에 설명).

왜 사용 패턴을 적용 하시겠습니까?

파이썬의 원칙 중 하나는 "consenting adults"입니다. 즉, 우리는 무언가를 사용하는 올바른 방법을 보여주고 문서화하는 것이 더 낫습니다. 사람들이 정당한 이유없이 그것을 파괴하지 않을 것이라고 믿는 것이 좋습니다. 워크 북없이 워크 시트를 만들거나 워크 시트가없는 셀을 만드는 것이 아무런 이점이 없다면 아무도 신경 쓰지 않을 것입니다.

또 다른 원칙은 duck typing입니다. 이것은 대략 느슨하게 지정된 인터페이스로 프로그래밍을 허용하는 것이 유리하다는 아이디어입니다. 누군가가 Workbook 메서드를 충분히 구현하는 클래스를 만들고 싶다면 Workbook 인스턴스를 사용할 수있는 곳이면 어디서든 사용하는 것이 좋습니다. 비록 그것이 내부적으로 아주 다른 것을하더라도.동의하는 성인들과 함께, 이것은 누군가가 이것을 시도하게하는 것이 유익 할 수 있음을 시사합니다. __init__ 메서드의 중첩 및 형식 기반 확인은이 문제를 해결합니다.

오용에게 난 그냥 덮여으로

을 방지하기 위해 시도, 나는 일반적으로 전혀이 일을하지 않는 것이 좋습니다. 그러나 그렇게하면 잘해야합니다. 대체 __init__ 기능의 제안 된 수표는 나쁜 습관을 조장하기 때문에 번거로운 작업입니다. Exception 유형을 예외로 throw하므로이를 catch하는 유일한 방법은 예외를 잡는 것입니다. 대신 인수의 유형을 확인 중이므로 TypeError과 같은 특정 예외를 던지거나 커스텀 하위 클래스 인 (더 큰 코드 본에서) 예외를 던집니다.

내가 WorksheetCells이 전혀 존재 이유를 물었다 의견에 동의 접근에

추가 개조하면 되겠 어. 마찬가지로, 도우미 클래스를 갖는 것은 거의 이점이 없습니다. 도우미 클래스가 구현 세부 사항이거나, 아마도 완전히 생략되었을 수도 있습니다. 간단하게는 이러한 구현의 세부 사항을 문서화 할 수 있도록하기 위해, 당신은 다음과 같습니다 코드 가능 __getitem__ 및/또는 __setitem__을 통해 하나 또는 둘 모두를 구현하는 것이 좋습니다 : 나는 가정을 만들고 있어요

worksheet = workbook['First Worksheet'] 
worksheet['A1'] = 'Foo...' 

을 그 실제 코드에서는 이미 설명한 방법보다 훨씬 많은 기능을 사용할 수 있습니다. __getitem__ 및/또는 __setitem__ 접근자를 계속 고려할 수도 있지만 노출되고 문서화 된 클래스가있는 다른 충분한 이유가 있습니다.

추가 기능의 일부로 기존 시트와 셀에 액세스하는 메서드를 완전히 만들 수있는 메서드와 메서드를 분리하는 것이 유용 할 수 있습니다.

언제 중첩 할 수 있습니까?

이 특정 사례는이 사례와 일치하지 않지만 유사한 인터페이스로 항목을 그룹화하는 것이 좋습니다. 예를 들어 Cell의 다른 하위 클래스를 인스턴스화해야하는 Worksheet의 여러 하위 클래스를 인스턴스화해야하는 Workbook의 하위 클래스가 여러 개 있다고 가정 해 보겠습니다. 클래스 상속 기계를 활용하여 클래스에 상속 기계를 저장하는 것이 유용 할 수 있습니다.

그러나이 시나리오에서도 클래스 자체를 중첩하지 않아도됩니다.

class Workbook: 
    # : : : 
    worksheet_type = Worksheet 
    # : : : 
    def worksheet(self, name): 
     return self.worksheet_type(self, name) 

class WorkbookVariant(Workbook): 
    # : : : 
    worksheet_type = WorkbookVariant 
관련 문제