Python 包装一个类';子类方法

Python 包装一个类';子类方法,python,mongodb,class,generics,Python,Mongodb,Class,Generics,我的主类定义如下: from pymongo import MongoClient from credentials import MongoMarketPlaceAuth client = MongoClient('mongodb://conn_url:conn_port/', ssl=True) db_connection = client.DB_NAME db_connection.authenticate(name=MongoMarketPlaceAuth.name, passwor

我的主类定义如下:

from pymongo import MongoClient
from credentials import MongoMarketPlaceAuth


client = MongoClient('mongodb://conn_url:conn_port/', ssl=True)
db_connection = client.DB_NAME
db_connection.authenticate(name=MongoMarketPlaceAuth.name, password=MongoMarketPlaceAuth.password)
MarketPlace = db_connection


class MarketPlaceObjects(object):

    def __init__(self, collection, fields={}, default_filter={}):
        self.collection_name = collection
        self.fields = fields
        self.default_filter = default_filter
        if MarketPlace[collection].count() == 0:
            raise Exception('Collection does not exist or has zero objects')
        self.objects = MarketPlace[collection]
然后我有一个继承我的主类的类:

class Products(MarketPlaceObjects):

    def __init__(self):
        super(Products, self).__init__(collection='FlatProduct')
这样使用时:

from products import Products
p = Products()
p.objects.find_one()
返回一个描述产品所有方面的词典。 我想做的是弄清楚

p.objects.find_one()
它可以返回一个产品对象(来自单个返回的词典)或一个产品对象列表(来自返回的词典列表),而不是返回词典或词典列表

因为我不确定如何用我自己的产品类包装PyMongo集合类的find_one()或find()方法,所以我的日子不好过


更新(2017-07-25): 这就是我最后做的。仍然需要一些优化:

市场通用类:

class CollectionObjectInstance(object):
    def __init__(self, response):
        for key, value in response.items():
            if isinstance(value, dict):
                self.__dict__[key] = CollectionObjectInstance(value)
            else:
                self.__dict__[key] = value


class CollectionObjectsManager(object):

    serializer = CollectionObjectInstance
    collection = None
    default_projection = {}
    default_filter = {}

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

    def find(self, filter={}, projection={}):
        filter = self.default_filter.update(filter)
        projection = self.default_projection.update(projection)
        res = self.collection.find(filter, projection)
        for o in res:
            yield self.serializer(o)

    def find_one(self, filter={}, projection={}):
        filter = self.default_filter.update(filter)
        projection = self.default_projection.update(projection)
        res = self.collection.find_one(filter, projection)
        return self.serializer(res)


class MarketPlaceCollection(object):

    collection_name = None
    serializer = None
    objects_manager = CollectionObjectsManager

    def __init__(self, *args, **kwargs):
        if self.collection_name is None:
            raise Exception('collection_name must be defined in class')
        if self.serializer is None:
            raise Exception('serializer must be defined in class')
        collection = MarketPlace[self.collection_name]
        if collection.count() == 0:
            raise Exception('Collection does not exist or has zero objects')
        self.collection = collection
        self.objects = self.objects_manager(**self.__dict__, **self.__class__.__dict__)
使用继承的产品实现:

from marketplace import MarketPlaceCollection, CollectionObjectInstance
from settings import BASE_URL


URL_SUFFIX = 'product/'
CASH_PRICE_FEE = 50
LEASE_TERM_WEEKS = 52


class Product(CollectionObjectInstance):

    def url(self):
        url = BASE_URL + URL_SUFFIX + self.slug
        return url


class Products(MarketPlaceCollection):

    collection_name = 'FlatProduct'
    serializer = Product

当您调用
p.objects
时,您将获得收藏列表本身,如
self.objects=MarketPlace[collection]
行中的属性所示。您的
产品
不再控制
.objects
属性中的方法或属性-它是pymongo返回的对象

因此,要控制
产品.objects
的方法和属性,您必须使用所需的方法创建另一个类,并在尝试检索
产品.objects
时返回该类的对象

尽管Python有“属性”装饰器和描述符协议,而且
对象
属性的更复杂自动化可以利用它们,但在这种情况下,可以以非常简单的方式实现。只需使用另一个类来接收集合,并通过在其中实现
\uuu getattr\uuuu
将其他属性代理到集合:

class ObjectList(object):
    def __init__(self, collection, cls):
        self.collection = collection
        self.cls = cls

    def find_one(self, *args, **kw):
        raw_list = self.collection.find_one(*arg, **kw)
        objects = [self.cls(**obj) for obj in raw_list]
        return objects[0] if len(objects) == 1 else objects

    def __getattr__(self, attr):
        """this is called automatically by Python when a
           normal attribute is not found  in this object
        """ 
        return getattr(self.collection, attr)


class MarketPlaceObjects(object):

    def __init__(self, collection, cls, fields=None, default_filter=None):
        self.collection_name = collection
        self.fields = fields if fields else {}
        self.default_filter = default_filter if defaut_filter else {}
        if MarketPlace[collection].count() == 0:
            raise Exception('Collection does not exist or has zero objects')
        self.objects = ObjectList(MarketPlace[collection], cls)

class Products(MarketPlaceObjects):

    def __init__(self):
        super(Products, self).__init__(collection='FlatProduct', cls=Product)

除了您的问题之外,Python中还有一个常见错误:您不应该使用像
{}
这样的可变对象作为函数或方法的默认参数。这是因为这些词汇表是在加载模块时创建的,并在每次调用该函数或方法时重复使用。使用
None
作为默认值,并在方法体中使用
if
语句将参数指定给空dict。True!我尽量避免使用ifs,因为我认为这会增加广告开销。您认为使用if然后分配空dict性能更好吗?谢谢!在我看到你的答案之前,我最终编写了一个解决方案。然而,我肯定会重写我的代码,以吸收您使用的一些技术。我的代码现在看起来不是pythonic哈哈。