복붙노트

[PYTHON] 클래스를 파이썬에서리스트처럼 동작하도록합니다.

PYTHON

클래스를 파이썬에서리스트처럼 동작하도록합니다.

나는 본질적으로 사물들의 목록 / 목록 인 수업을 가지고있다. 그러나이 목록에 몇 가지 추가 기능을 추가하고 싶습니다. 내가 원했던 것은 다음과 같다.

현재 다음 접근 방식을 사용하고 있습니다.

class MyFancyList:
  def __iter__(self): 
    return self.li 
  def fancyFunc(self):
    # do something fancy

이것은 [e for li]와 같은 반복자로 사용하는 것은 괜찮지 만, li.expand (...)와 같은 전체 목록 비헤이비어가 없습니다.

첫 번째 추측은 MyFancyList에 목록을 상속하는 것입니다. 그러나 권장되는 파이썬 적 방법은 무엇입니까? 그렇다면 고려해야 할 사항은 무엇입니까? 그렇다면 더 나은 접근 방법은 무엇입니까?

해결법

  1. ==============================

    1.목록 비헤이비어의 일부만을 원한다면 composition (즉, 인스턴스가 실제 목록에 대한 참조를 보유)을 사용하고 원하는 비헤이비어에 필요한 메소드 만 구현하십시오. 이러한 메소드는 작업을 실제 목록에 위임해야합니다. 예를 들어, 클래스의 모든 인스턴스가 참조를 보유해야합니다.

    목록 비헤이비어의 일부만을 원한다면 composition (즉, 인스턴스가 실제 목록에 대한 참조를 보유)을 사용하고 원하는 비헤이비어에 필요한 메소드 만 구현하십시오. 이러한 메소드는 작업을 실제 목록에 위임해야합니다. 예를 들어, 클래스의 모든 인스턴스가 참조를 보유해야합니다.

    def __getitem__(self, item):
        return self.li[item] # delegate to li.__getitem__
    

    혼자서 __getitem__을 구현하면 놀랄만큼 많은 양의 기능이 제공됩니다 (예 : 반복 및 분할).

    >>> class WrappedList:
    ...     def __init__(self, lst):
    ...         self._lst = lst
    ...     def __getitem__(self, item):
    ...         return self._lst[item]
    ... 
    >>> w = WrappedList([1, 2, 3])
    >>> for x in w:
    ...     x
    ... 
    1
    2
    3
    >>> w[1:]
    [2, 3]
    

    목록의 모든 동작을 원하면 collections.UserList에서 상속을받습니다. UserList는 목록 데이터 유형의 전체 Python 구현입니다.

    그렇다면 목록에서 직접 상속받지 않는 이유는 무엇입니까?

    목록 (또는 C로 작성된 다른 내장 함수)에서 직접 상속하는 주요 문제점 중 하나는 내장 함수의 코드가 사용자가 정의한 클래스에서 재정의 된 특수 메소드를 호출 할 수도 있고 호출하지 않을 수도 있다는 것입니다. pypy 문서에서 발췌 한 내용은 다음과 같습니다.

    루치아노 라 말로 (Luciano Ramalho)의 Fluent Python (Fluent Python) 351 쪽의 또 다른 인용문 :

    ... and more, page 370+ :

    조금 놀고 나면, 목록 내장 문제는 덜 중요해 보입니다. (파이썬 3.4에서 잠시 깰려고했으나 예기치 않은 행동을 발견하지 못했지만) 원칙적으로 발생할 수 있으므로 여기에 dict 및 UserDict가 있습니다.

    >>> class MyDict(dict):
    ...     def __setitem__(self, key, value):
    ...         super().__setitem__(key, [value])
    ... 
    >>> d = MyDict(a=1)
    >>> d
    {'a': 1}
    
    >>> class MyUserDict(UserDict):
    ...     def __setitem__(self, key, value):
    ...         super().__setitem__(key, [value])
    ... 
    >>> m = MyUserDict(a=1)
    >>> m
    {'a': [1]}
    

    보시다시피, dict의 __init__ 메소드는 무시 된 __setitem__ 메소드를 무시했지만 UserDict의 __init__ 메소드는 무시했습니다.

  2. ==============================

    2.가장 간단한 해결책은 목록 클래스에서 상속하는 것입니다.

    가장 간단한 해결책은 목록 클래스에서 상속하는 것입니다.

    class MyFancyList(list):
        def fancyFunc(self):
            # do something fancy
    

    그런 다음 MyFancyList 유형을 목록으로 사용하고 특정 방법을 사용할 수 있습니다.

    상속은 개체와 목록간에 강력한 결합을 가져옵니다. 기본적으로 구현하는 접근 방식은 프록시 객체입니다. 크게 사용하는 방법은 객체를 사용하는 방법에 따라 다릅니다. 그것이 목록이어야한다면, 상속은 아마도 좋은 선택 일 것입니다.

    편집 : @ acdr에 의해 지적한대로 목록 복사 대신 일부 메서드는 MyFancyList 대신 목록을 반환하려면 재정의해야합니다.

    이를 구현하는 간단한 방법은 다음과 같습니다.

    class MyFancyList(list):
        def fancyFunc(self):
            # do something fancy
        def __add__(self, *args, **kwargs):
            return MyFancyList(super().__add__(*args, **kwargs))
    
  3. ==============================

    3.게시물 (fancyPrint, findAMetric)에 포함 된 두 예제 메서드를 기반으로 목록에 추가 상태를 저장해야하는 것은 아닙니다. 이 경우 무료 함수로 선언하고 하위 유형 지정을 모두 무시하는 것이 가장 좋습니다. 이것은 list 대 UserList, __add__에 대한 반환 유형, 예기치 않은 Liskov 문제, & c와 같은 연약한 가장자리 경우와 같은 문제를 완전히 피합니다. 대신 함수를 작성하고 결과에 대한 단위 테스트를 작성할 수 있으며 모든 것이 의도 한대로 정확하게 작동 할 것이라는 확신을 가질 수 있습니다.

    게시물 (fancyPrint, findAMetric)에 포함 된 두 예제 메서드를 기반으로 목록에 추가 상태를 저장해야하는 것은 아닙니다. 이 경우 무료 함수로 선언하고 하위 유형 지정을 모두 무시하는 것이 가장 좋습니다. 이것은 list 대 UserList, __add__에 대한 반환 유형, 예기치 않은 Liskov 문제, & c와 같은 연약한 가장자리 경우와 같은 문제를 완전히 피합니다. 대신 함수를 작성하고 결과에 대한 단위 테스트를 작성할 수 있으며 모든 것이 의도 한대로 정확하게 작동 할 것이라는 확신을 가질 수 있습니다.

    이점은 추가 기능을 사용하지 않고 생성자 표현식과 같은 반복 가능한 유형에서 함수가 작동 함을 의미합니다.

  4. ==============================

    4.모든 목록 방법을 재정의하고 싶지 않으면 다음 방법을 제안합니다.

    모든 목록 방법을 재정의하고 싶지 않으면 다음 방법을 제안합니다.

    class MyList:
      def __init__(self, list_):
        self.li = list_
      def __getattr__(self, method):
        return getattr(self.li, method)
    

    이렇게하면 append, extend 등의 메서드가 상자 밖으로 나옵니다. 그러나 magic methods (예 : __len__, __getitem__ 등)는이 경우 작동하지 않으므로 최소한 다음과 같이 다시 선언해야합니다.

    class MyList:
      def __init__(self, list_):
        self.li = list_
      def __getattr__(self, method):
        return getattr(self.li, method)
      def __len__(self):
        return len(self.li)
      def __getitem__(self, item):
        return self.li[item]
      def fancyPrint(self):
        # do whatever you want...
    

    이 경우 목록의 메서드를 오버라이드 (예 : 확장)하려는 경우 호출이 __getattr__ 메서드를 통과하지 않도록 자신의 메서드를 선언 할 수 있습니다. 예를 들면 :

    class MyList:
      def __init__(self, list_):
        self.li = list_
      def __getattr__(self, method):
        return getattr(self.li, method)
      def __len__(self):
        return len(self.li)
      def __getitem__(self, item):
        return self.li[item]
      def fancyPrint(self):
        # do whatever you want...
      def extend(self, list_):
        # your own version of extend
    
  5. from https://stackoverflow.com/questions/36688966/let-a-class-behave-like-its-a-list-in-python by cc-by-sa and MIT license