如何为具有相同结构的方法创建通用方法?

时间:2017-08-28 16:39:39

标签: python algorithm python-3.x design-patterns

我在CommentsService类中有以下方法:

async def background_job_auto_approve(self):
    while True:
        new = get_comments_by_status(CommentStatus.NEW.value)
        pending = get_comments_by_status(CommentStatus.PENDING.value)
        all = new + pending
        for comment in all:
            if check_it_auto_approve(item=comment):
                await self.auto_approve(comment_id=comment['comment_id'],
                                        alert_id=comment['alert_id'])
                yield comment
        await asyncio.sleep(self.check_expire_seconds)

但是我在AlertsService中的方法完全相同:

async def background_job_auto_approve(self):
    while True:
        new = get_alerts_by_status(AlertStatus.NEW.value)
        pending = get_alerts_by_status(AlertStatus.PENDING.value)
        all = new + pending
        for alert in all:
            if check_it_auto_approve(item=alert):
                await self.auto_approve(alert_id=alert['alert_id'])
                yield alert
        await asyncio.sleep(self.check_expire_seconds)

如何避免代码重复?我对这些类中的其他方法也有同样的问题。

2 个答案:

答案 0 :(得分:0)

如果没有其他代码库/上下文的目的,很难想出一个真正通用的方法来实现这一点。但是,如果您可以巩固您似乎正在使用的模式([objectname]Status[objectname]_idget_[objectname]s_by_status),这将正常工作。我建议为auto_approve

等方法添加其他类
class AutoApprovalLoop(object):
    def __init__(self, method_ptr=None, cls_ptr=None, approval_key=None):
        self.method_ptr = method_ptr     # one of get_comments_by_status or get_alerts_by_status
        self.cls_ptr = cls_ptr           # one of AlertStatus or CommentStatus
        self.approval_key = approval_key # one of 'comment_id' or 'alert_id'

    def loop(self):
        while True:
            new = self.method_ptr(self.cls_ptr.NEW.value)
            pending = self.method_ptr(self.cls_ptr.PENDING.value) 
            all = new + pending
            for item in all:
                if check_it_auto_approve(item=item)
                await self.auto_approve(**{self.approval_key: item[self.approval_key]})
                yield item
            await asyncio.sleep(self.check_expire_seconds)

答案 1 :(得分:0)

一点警告。我不是在Python 3上,当你将调用转发给另一个函数时,我不完全确定asyncio和async关键字的行为。

首先要做的是:传入第一个和第二个函数之间不同的变量。即:哪个函数返回您的等待批准及其状态标准对象。

然后,使用准备好的operator.itemgetter一般从每个obj中检索密钥。如果itemgetter有几个要查找的字段,则返回一个元组,否则为标量 - 这就是isinstance(tu, tuple)尝试规范化的内容。使用*tu解压缩 auto_approve 电话。

使用

,使用位置调用 check_it_auto_approve self.auto_approve
async def generic_background_job_auto_approve(self, get_obj_by_status, ObjStatus, myitemgetter):
    while True:
        new = get_obj_by_status(ObjStatus.NEW.value)
        pending = get_obj_by_status(ObjStatus.PENDING.value)
        all = new + pending
        for obj in all:
            #call with positional if possible.
            if check_it_auto_approve(obj):
                tu = myitemgetter(obj)
                #itemgetters return a tuple if multiple keys, a scalar if only one
                tu = tu if isinstance(tu, tuple) else (tu,)
                #assuming your auto_approve does ok with positional variables, doesn't need named ones
                await self.auto_approve(*tu)
                yield obj
        await asyncio.sleep(self.check_expire_seconds)
好的,现在您已准备好调用通用代码

import operator
async def call_for_comment(self):
    return self.generic_background_job_auto_approve(
        get_obj_by_status=get_comments_by_status
        ,ObjStatus=CommentStatus
        ,myitemgetter=operator.itemgetter('comment_id','alert_id')
        )

async def call_for_alert(self):
    return self.generic_background_job_auto_approve(
        get_obj_by_status=get_alerts_by_status
        ,ObjStatus=AlertStatus
        ,myitemgetter=operator.itemgetter('alert_id')
        )

如果你需要使用命名参数调用,你可以传入一个返回所需变量的字典的函数,然后调用** mydict来调用。