且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

抽象类和 PyMongo;无法实例化抽象类

更新时间:2023-01-11 10:05:41

这真的不是 ABC 的问题,而是 PyMongo 的问题.此处存在一个问题.pymongo 似乎覆盖了 __getattr__ 以返回某种数据库类.这意味着 host.__isabstractmethod__ 返回一个 Database 对象,这在布尔上下文中为真.这导致 ABCMeta 认为 host 是一个抽象方法:

>>>布尔(主机.__isabstractmethod__)真的

问题报告中描述的解决方法是在您的对象上手动设置 host.__isabstractmethod__ = False.关于该问题的最后一条评论表明已针对 pymongo 3.0 进行了修复.

I created the empty abstract class AbstractStorage and inherited the Storage class from it:

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()

I expected the output to be

True
__init__

however, the one I'm getting is

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

The problem (apparently) goes away if I remove metaclass=abc.ABCMeta (so that AbstractStorage becomes an ordinary class) and/or if I set dbh to some other value.

What's going on here?

This isn't really a problem with ABCs, it's a problem with PyMongo. There is an issue about it here. It seems that pymongo overrides __getattr__ to return some sort of database class. This means that host.__isabstractmethod__ returns a Database object, which is true in a boolean context. This cause ABCMeta to believe that host is an abstract method:

>>> bool(host.__isabstractmethod__)
True

The workaround described in the issue report is to manually set host.__isabstractmethod__ = False on your object. The last comment on the issue suggests a fix has been put in for pymongo 3.0.