2010-01-09 3 views
29
여기

은 이론적 인 부분과 더불어, 두 가지 질문, 그리고 실제 하나Subclassing dict : __ init __()을 호출해야합니까?

하는 서브 클래스 DICT :

class ImageDB(dict): 
    def __init__(self, directory): 
     dict.__init__(self) # Necessary?? 
     ... 

해야 dict.__init__(self) 호출, 단지 "안전"조치로 (예를 들어, 경우 중요하지 않은 구현 세부 사항이 있습니다)? dict.__init__()이 아니고이라면 코드가 파이썬의 차기 버전에서 깨질 위험이 있습니까? 나는 한 가지 또는 다른 것을하는 근본적인 이유를 찾고있다. (실제로는 dict.__init__()가 안전하다).

내 생각에 ImageDB.__init__(self, directory)이 호출 될 때 self는 이미 새 빈 dict 객체이며 따라서 dict.__init__ (처음에는 dict이 비어 있어야 함)을 호출 할 필요가 없습니다. 이 올바른지?

편집 :

위의 근본적인 질문 뒤에 더 실제적인 질문은 다음과 같다. 나는 db [...] 구문을 꽤 자주 사용하기 때문에 (db.contents [...] 항상하는 대신에) dict를 서브 클래 싱하는 것을 생각하고 있었다. 객체의 유일한 데이터 (속성)는 정말로 실제로 dict입니다. 데이터베이스에 몇 가지 메소드 (예 : get_image_by_name() 또는 get_image_by_code())를 추가하고 이미지 데이터베이스가 포함 된 디렉토리로 정의되기 때문에 __init__() 만 덮어 쓰려고합니다. 요약에서

의 (실제) 문제가 될 수있다 : 초기화가 다른 것을 제외하고, 사전처럼 작동 뭔가 좋은 구현이 무엇인지 (그것은 단지 디렉토리 이름을 사용), 그리고 추가를 가지고 행동 양식?

많은 답변에서 "공장"이 언급되었습니다. 그래서 그 모든 종기가 내려 다닌다 : 하위 클래스 dict, override __init__() 및 메서드를 추가 할 또는 메서드를 추가하는 dict 반환하는 (팩토리) 함수를 작성합니까? 첫 번째 솔루션을 선호하는 경향이 있습니다. 왜냐하면 팩토리 함수가 유형에 추가 의미 및 메소드가 있음을 나타내지 않는 객체를 반환하기 때문입니다.하지만 어떻게 생각하십니까?

편집 2 : 나는 새 클래스는 "사전에없는"경우, 특히 그 __init__ 방법은 동일을 할 수없는 경우 DICT를 서브하는 것은 좋은 생각이 아니라고 모두의 대답에서 수집

인수는 dict의 __init__ (위의 "실제 질문"의 경우)입니다. 즉, 내가 올바르게 이해한다면 합의는 다음과 같을 것입니다. 하위 클래스를 만들 때 모든 메소드 (초기화 포함)는 기본 클래스 메소드와 동일한 서명을 가져야합니다. 이를 통해 isinstance (subclass_instance, dict)는 예를 들어 dict.__init__()처럼 subclass_instance.__init__()을 사용할 수 있음을 보장합니다.

또 다른 실질적인 질문이 나옵니다. 초기화 방법을 제외하고는 어떻게 dict와 비슷한 클래스를 구현해야합니까? 서브 클래 싱하지 않고? 이렇게하면 약간의 번거로운 상용구 코드가 필요합니다.

+0

공장 기능이 있습니다. * instance * 비헤이비어를 사용자 정의해야하는 경우 서브 클래스를 작성할 수 있습니다. * initialization *을 오버라이드하고 싶다면 인스턴스를 표준 클래스와 다르지 않으므로 아무 것도 서브 클래 싱 할 필요가 없습니다. __init__은 인스턴스의 인터페이스 부분이 아니라 클래스의 인터페이스 부분으로 간주됩니다. –

+0

이 문제에 관해서는 dict를 하위 클래스로 분류하는 대신 ImageDB에'__getitem__' 메서드를 추가하는 것이 가장 좋습니다. 왜냐하면 그것은 딕트가 아니기 때문입니다. 이렇게하면 클래스에 부적합한'pop()'과 같은 모든 메소드를 사용하지 않고도 원하는 것을 할 수 있습니다. –

+0

@gs : 팝에 대한 좋은 지적; 적어도 현재로서는 관계가 없습니다 (데이터베이스 내용은 초기화시에만 정의됩니다). 구현이 필요한 기능을 완벽하게 충족시키는 것이 실제로 가장 좋은 방법이라고 생각합니다. – EOL

답변

15

서브 클래 싱 할 때 dict.__init__(self)을 호출해야합니다. 실제로, 당신은 dict에서 정확히 무슨 일이 일어나고 있는지를 알지 못한다. (그것은 내장되어 있기 때문에) 버전과 구현에 따라 다를 수있다. dict이 내부 데이터 구조를 가지고있는 곳을 알 수 없으므로 부적절한 동작을 초래할 수 있습니다.

그런데, 당신은 이 무엇을 말하지 않았습니까? 할; dict (매핑) 동작을 사용하는 클래스가 필요하고 실제로 dict가 필요하지 않은 경우 (예 : 소프트웨어에 isinstance(x, dict)을 수행하는 코드가없는 경우) UserDict.UserDict 또는 UserDict.DictMixin을 사용하는 것이 더 나을 것입니다. Python> = 2.6 인 경우 < = 2.5 또는 collections.MutableMapping 인 경우. 그것들은 당신의 수업에 우수한 dict 행동을 제공 할 것입니다.

편집 : 다른 의견에서 당신이 어떤 dict의 방법도 무시하지 않고 있다고 읽었습니다! 그렇다면 전혀 서브 클래 싱을 수행 할 필요가 없습니다.

def createImageDb(directory): 
    d = {} 
    # do something to fill in the dict 
    return d 

편집 2 : 새로운 방법을 추가 DICT에서 상속 할,하지만 당신은 하나를 오버라이드 (override) 할 필요가 없습니다. 좋은 선택은 다음과 같을 수 있습니다.

class MyContainer(dict): 
    def newmethod1(self, args): 
     pass 

    def newmethod2(self, args2): 
     pass 


def createImageDb(directory): 
    d = MyContainer() 
    # fill the container 
    return d 

그런데 어떤 방법을 추가하고 있습니까? 당신은 좋은 추상화를 만들고 있습니까? 어쩌면 당신이 필요로하는 메소드를 정의하고 내부적으로 "정상적인"dict를 사용하는 클래스를 사용하는 것이 더 나을 것입니다.

공장 FUNC : 그것은 단순히 함수에 인스턴스의 건설을 위임하는 대신 오버라이드 (override)/그 생성자를 변경하는 방법 http://en.wikipedia.org/wiki/Factory_method_pattern

.

+2

+1 : 서브 클래 싱 할 필요가 없을 때 서브 클래 싱을하는 것이 나쁜 생각이다. 공장은 훨씬 좋다. –

+0

dict 메서드를 재정의하지 않더라도 새 클래스에는 추가 메서드가 있습니다 ... (공장을 조사 중이므로 포인터를 가져 주셔서 감사합니다!) – EOL

+0

UserDict에 대해 잘 모르겠습니다. 설명서에 "This module also reads 이 클래스의 필요성은 dict (Python 버전 2.2부터 사용할 수있게 된 기능)에서 직접 하위 클래스를 만드는 기능으로 대체되었습니다. " – EOL

2

PEP 372은 collections 모듈에 주문 된 dict를 추가하는 것을 다룹니다.

경고 서브 클래스 "dict"는 중요하지 않은 작업이며 많은 구현이 모든 메서드를 제대로 재정 의하여 예기치 않은 결과가 발생할 수 있다고 경고합니다.이를 바탕으로

+class OrderedDict(dict, MutableMapping): 
+ def __init__(self, *args, **kwds): 
+  if len(args) > 1: 
+   raise TypeError('expected at most 1 arguments, got %d' % len(args)) 
+  if not hasattr(self, '_keys'): 
+   self._keys = [] 
+  self.update(*args, **kwds) 

, 그것은 dict.__init__()처럼 보이는 호출 할 필요가 없습니다

"

는이 제안 (그리고 가능) python3.1에 patch이처럼 보이는 __init__을 사용합니다. 편집 :dict의 메소드를 재정의하거나 확장하지 않으면 Alan Franzoni에 동의합니다. 서브 클래스 화보다는 dict 팩터를 사용하십시오.

def makeImageDB(*args,**kwargs): 
    d = {} 
    # modify d 
    return d 
+0

이것은 흥미 롭습니다. 파이썬 3.1에서는'dict .__ init __()'을 호출하지 않는 것이 안전하지만 미래는 어떻게 될까요? ImageDB에서는 어떤 메소드도 오버라이드하지 않기 때문에 하위 클래스 화는 매우 안전합니다. 초기화 만이 특별하다 (dict를 빌드한다). – EOL

+0

죄송합니다 EOL, 나는 당신을 팔로우 않을 것입니다. 제 생각에는 파이썬 3.1이 미래입니다 ... :) – unutbu

+0

init이 실제로하는 일을 고려하십시오. 그것은 모든 args와 키워드로 사전을 갱신합니다. 그것은 클래스가해야 할 일이기 때문에, dict. \ _ \ _ init __ (self, * args, ** kwds)를 호출하는 것이 아마 당신을 대신 할 것입니다. 그렇지 않으면 OrderedDict와 같이 self.update를 호출해야합니다. 않습니다. –

10

일반적으로 기본 클래스 '__init__을 호출하여 여기에서 예외를 작성해야합니다.

하나는 __init__을 무시하거나 __init__ 전화 기본 클래스 __init__를 대체해야하는 경우이 인수에 대해 걱정하는 경우, 당신이 빈 DICT 예를 들어, 원하는 경우 * 인수, ** kwargs로 또는 아무것도를 전달하지 않습니다

class MyDict(dict): 
    def __init__(self, *args, **kwargs): 
     myparam = kwargs.pop('myparam', '') 
     dict.__init__(self, *args, **kwargs) 
우리는 일을하거나 일을하지 않는 것없는 baseclass 가정해서는 안

, 그것은 __init__

+0

dict'__init__ '을 호출하는 것은 실제로 무엇입니까? 나는 현재하고있다. 아무런 인자도없이 호출하는 것처럼 보이지 않으므로 파이썬에 대한 근본적인 사실이 궁금해서 전화하지 않아도됩니다. – EOL

+0

@EOL, IMO 달리해야할 매우 강한 이유가있을 때까지는 baseclass __init__을 호출하지 않는 것이 명백합니다. –

+0

@Anurag : 당신의 요지를 봅니다. 파이썬에 대한 지식을 조금 더 넓히려하고 있는데, (다른 인자가없는)'dict .__ init __ (self)'를 호출하지 않는 "매우 강한 이유"가 존재하는지 궁금해했다. 결코 아무것도하지 마라 "). – EOL

3

기본 클래스를 호출 딕셔너리를 서브 클래스 때 세척에주의하지 않는 잘못; 예를 들어, 2.7에서 __getnewargs__, 그리고 이전 버전에서는 __getstate__ __setstate__이 필요합니다. (나는 이유를 모른다.)

class Dotdict(dict): 
    """ d.key == d["key"] """ 

    def __init__(self, *args, **kwargs): 
     dict.__init__(self, *args, **kwargs) 
     self.__dict__ = self 

    def __getnewargs__(self): # for cPickle.dump(d, file, protocol=-1) 
     return tuple(self) 
관련 문제