2015-01-28 8 views
1

를 인스턴스화 할 수 없습니다 나는 빈 추상 클래스 AbstractStorage를 생성하고 여기에서 Storage 클래스 상속 : I 출력 그러나추상 클래스와 PyMongo; 추상 클래스를

True 
__init__ 

될 것으로 예상 난 하나를

import abc 
import pymongo as mongo 

host = mongo.MongoClient() 

print(host.alive()) # True 

class AbstractStorage(metaclass=abc.ABCMeta): 
    pass 

class Storage(AbstractStorage): 
    dbh = host 
    def __init__(self): 
     print('__init__') 

Storage() 

을 점점

True 
Traceback (most recent call last): 
    File "/home/vaultah/run.py", line 16, in <module> 
    Storage() 
TypeError: Can't instantiate abstract class Storage with abstract methods dbh 

metaclass=abc.ABCMeta (s)을 제거하면 문제가 (분명히) 사라집니다. o AbstractStorage은 일반 클래스가됩니다) 그리고/또는 dbh을 다른 값으로 설정하면됩니다.

여기 무슨 일 이니?

답변

4

이것은 ABC에서 실제로 문제가되지 않습니다. PyMongo에는 문제가 있습니다. 문제는 here입니다. pymongo가 __getattr__을 재정의하여 데이터베이스 클래스를 반환하는 것으로 보입니다. 즉, host.__isabstractmethod__은 부울 컨텍스트에서 true 인 Database 개체를 반환합니다.

>>> bool(host.__isabstractmethod__) 
True 

문제 보고서에 설명 된 해결 방법은 수동으로 객체에 host.__isabstractmethod__ = False를 설정하는 것입니다 :이 원인 ABCMeta는 host는 추상적 인 방법이라고 생각합니다. 이 문제에 대한 마지막 의견은 pymongo 3.0에 대한 수정을 제안합니다.

+0

와우 당신은 하나가 정확한 오류 당신이 얻고을 만들어 볼 수 있습니다. 고마워 :) 아마도 mongo.version_tuple <(3, 0) : dbh .__ isabstractmethod__ = False'와 같은 것을 클래스 정의에서 작성해야합니다. – vaultah

+0

@vaultah : 그렇게 할 수 있습니다. 그래도 상위 버전에서도 그 속성을 설정하는 것이 위험 할 것이라고 생각하지 않습니다. 그것은 조금 이상하게 보일 것입니다. – BrenBarn

+0

실제 버그 보고서를 찾았습니다. (다른 뉴스에서는 생각했던 것보다 _way_을 더 느리게 입력했습니다.) –

1

짧은 버전

mongo.MongoClient는 것으로 보인다 객체를 반환 (입니까?) 당신이 다음 Storagedbh 필드에 할당 추상적 인 방법. 이렇게하면 Storage이 추상 클래스가되므로 인스턴스화하면 TypeError이됩니다.

pymongo이 없으므로 ABCMeta으로 처리되는 것보다 MongoClient에 대해 더 알려 드릴 수는 없습니다.

롱 버전

ABCMeta.__new__ 방법은 만드는 것 새로운 클래스의 각 필드 내부 보인다. True (또는 "true-like") __isabstractmethod__ 필드가있는 필드는 모두 추상 메소드로 간주됩니다. 클래스에 이 있고 오버라이드되지 않은 추상 메소드가있는 경우 전체 클래스는 추상으로 간주되므로 인스턴스화하려는 모든 시도는 오류입니다. 이전 버전에서

표준 라이브러리의 abc.py :이 abc.ABCMeta 클래스 문서에 언급되지 않은

def __new__(mcls, name, bases, namespace): 
    cls = super().__new__(mcls, name, bases, namespace) 
    # Compute set of abstract method names 
    abstracts = {name 
       for name, value in namespace.items() 
       if getattr(value, "__isabstractmethod__", False)} 
    # ... 
    cls.__abstractmethods__ = frozenset(abstracts) 
    # ... 

하지만 @abc.abstractmethod 장식에 따라 약간 낮은 : 위해

에 추상 기본 클래스 기계와 정확하게 상호 작용하면, 설명자는 __isabstractmethod__을 사용하여 자신을 추상으로 식별해야합니다. 기술자를 구성하는 데 사용 된 방법 중 하나라도 추상적 인 경우 일반적으로이 속성은 True이어야합니다.

예 나는 __isabstractmethod__ 속성 및 AbstractStorage 두 가정 콘크리트 서브 클래스와 가짜 "추상보고"클래스를 만들었습니다. 이 생산 실행

#!/usr/bin/env python3 


import abc 
# I don't have pymongo, so I have to fake it. See CounterfeitAbstractMethod. 
#import pymongo as mongo 


class CounterfeitAbstractMethod(): 
    """ 
    This class appears to be an abstract method to the abc.ABCMeta.__new__ 
    method. 

    Normally, finding an abstract method in a class's namespace means 
    that class is also abstract, so instantiating that class is an 
    error. 

    If a class derived from abc.ABCMeta has an instance of 
    CounterfeitAbstractMethod as a value anywhere in its namespace 
    dictionary, any attempt to instantiate that class will raise a 
    TypeError: Can't instantiate abstract class <classname> with 
    abstract method <fieldname>. 
    """ 
    __isabstractmethod__ = True 


class AbstractStorage(metaclass=abc.ABCMeta): 

    def __init__(self): 
     """ 
     Do-nothing initializer that prints the name of the (sub)class 
     being initialized. 
     """ 
     print(self.__class__.__name__ + ".__init__ executing.") 
     return 


class ConcreteStorage(AbstractStorage): 
    """ 
    A concrete class that also _appears_ concrete to abc.ABCMeta. This 
    class can be instantiated normally. 
    """ 
    whatever = "Anything that doesn't appear to be an abstract method will do." 


class BogusStorage(AbstractStorage): 
    """ 
    This is (supposedly) a concrete class, but its whatever field appears 
    to be an abstract method, making this whole class abstract --- 
    abc.ABCMeta will refuse to construct any this class. 
    """ 
    #whatever = mongo.MongoClient('localhost', 27017) 
    whatever = CounterfeitAbstractMethod() 


def main(): 
    """ 
    Print details of the ConcreteStorage and BogusStorage classes. 
    """ 
    for cls in ConcreteStorage, BogusStorage: 
     print(cls.__name__ + ":") 
     print(" whatever field: " + str(cls.whatever)) 
     print(" abstract methods: " + str(cls.__abstractmethods__)) 
     print(" Instantiating...") 
     print(" ", end="") 
     # KABOOM! Instantiating BogusStorage will raise a TypeError, 
     # because it appears to be an _abstract_ class. 
     instance = cls() 
     print(" instance: " + str(instance)) 
     print() 
    return 


if "__main__" == __name__: 
    main() 

:

$ ./storage.py 
ConcreteStorage: 
    whatever field: Anything that doesn't appear to be an abstract method will do. 
    abstract methods: frozenset() 
    Instantiating... 
    ConcreteStorage.__init__ executing. 
    instance: <__main__.ConcreteStorage object at 0x253afd0> 

BogusStorage: 
    whatever field: <__main__.CounterfeitAbstractMethod object at 0x253ad50> 
    abstract methods: frozenset({'whatever'}) 
    Instantiating... 
    Traceback (most recent call last): 
    File "./storage.py", line 75, in <module> 
    main() 
    File "./storage.py", line 68, in main 
    instance = cls() 
TypeError: Can't instantiate abstract class BogusStorage with abstract methods whatever 
관련 문제