Python类DRY

时间:2018-09-18 12:14:50

标签: python class

我编写了以下python类。现在,我在每个函数中都重复if settings.MIXPANEL_ID:,我想知道是否有更好的方法编写此类?

class MixpanelClass:
    def __init__(self):
        if settings.MIXPANEL_ID:
            mp = Mixpanel(settings.MIXPANEL_ID)

    def mp_track_event(user_id, event, property=None):
        if settings.MIXPANEL_ID:
            mp.track(user_id, event, property)

    def mp_people_set(user_id, property):
        if settings.MIXPANEL_ID:
            mp.people_set(user_id, property)

    def mp_people_increment(user_id, property):
        if settings.MIXPANEL_ID:
            mp.people_increment(user_id, property)

    def mp_people_track_charge(user_id, revenue, property):
        if settings.MIXPANEL_ID:
            mp.people_track_charge(user_id, revenue, property)

    def mp_get_distinct_id(request):
        if settings.MIXPANEL_ID:
            mp_cookie_name = 'mp_' + settings.MIXPANEL_ID + '_mixpanel'
            mp_cookie = request.COOKIES.get(mp_cookie_name, None)
            if mp_cookie:
                unquoted = unquote(mp_cookie)
                dictionary = literal_eval(unquoted)
                return dictionary.get('distinct_id')

3 个答案:

答案 0 :(得分:1)

您可以使用类装饰器(如果需要,可以装饰单个方法):

class enable_if:
    def __init__(self, condition):
        self.condition = condition

    def __call__(self, obj):
        if isinstance(obj, type):
            return self.decorate_class(obj)
        return self.decorate_callable(obj)

    def decorate_class(self, cls):
        for name in dir(cls):
            attr = getattr(cls, name)
            if callable(attr) and (name == '__init__' or not name.startswith('__') and not name.endswith('__')):
                setattr(cls, name, self.decorate_callable(attr))
        return cls

    def decorate_callable(self, func):
        def wrapper(obj, *args, **kwargs):
            if self.condition:
                return func(obj, *args, **kwargs)
        return wrapper

这样可以:

@enable_if(settings.MIXPANEL_ID)
class MixpanelClass:
    def __init__(self):
        mp = Mixpanel(settings.MIXPANEL_ID)

    def mp_track_event(user_id, event, property=None):
        mp.track(user_id, event, property)

    def mp_people_set(user_id, property):
        mp.people_set(user_id, property)

    def mp_people_increment(user_id, property):
        mp.people_increment(user_id, property)

    def mp_people_track_charge(user_id, revenue, property):
        mp.people_track_charge(user_id, revenue, property)

    def mp_get_distinct_id(request):
        mp_cookie_name = 'mp_' + settings.MIXPANEL_ID + '_mixpanel'
        mp_cookie = request.COOKIES.get(mp_cookie_name, None)
        if mp_cookie:
            unquoted = unquote(mp_cookie)
            dictionary = literal_eval(unquoted)
            return dictionary.get('distinct_id')

如果__init__MixpanelClass,则settings.MIXPANEL_ID类的每个用户方法和False方法将被禁用。

答案 1 :(得分:0)

我要提个建议,但是,我认为这仍然需要原始海报中的更多信息。


解决方案1:检入通用方法

您可以在要调用的方法上实现一个泛型函数。您必须将函数/方法作为参数传递给该函数

例如

def act_if_MIXPANEL_ID(self, method, *args, **kwargs):
    """Calls the method METHOD with the given args and kwargs"""
    if settings.MIXPANEL_ID:
        method(*args, **kwargs)

然后您可以使用以下代码调用此方法:

class MixpanelClass(object):

    # here goes the rest of your code

    def mp_track_event(self, user_id, event, property=None):
        self.act_if_MIXPANEL_ID(self.mp.track, user_id, event, property)


    def act_if_MIXPANEL_ID(self, method, *args, **kwargs):
        """Calls the method METHOD with the given args and kwargs"""
        if settings.MIXPANEL_ID:
            method(*args, **kwargs)

PS:我可以自由地将静态变量转换为标准方法。


解决方案2:装饰器

或者,您可以定义一个装饰器并使用它来注释您的方法。在对该方法的任何调用之前,装饰器将被调用。 它将调用做检查(在本例中打印其他内容)

有关装饰器的快速介绍,请参见此处:Intro

# This is the decorator
def check_settings(func):
    def wrapper(*args, **kwargs):
        if settings.MIXPANEL_ID:
            func(*args, **kwargs)
        else:
            print("Check for settings.MIXPANEL_ID failed.")
    return wrapper

class MixpanelClass:
    def __init__(self):
        if settings.MIXPANEL_ID:
            mp = Mixpanel(settings.MIXPANEL_ID)

    @check_settings
    def mp_track_event(user_id, event, property=None):
        mp.track(user_id, event, property)

    @check_settings
    def mp_people_set(user_id, property):
        mp.people_set(user_id, property)

    @check_settings
    def mp_people_increment(user_id, property):
        mp.people_increment(user_id, property)

答案 2 :(得分:0)

如果未设置settings.MIXPANEL_ID,则似乎没有任何意义拥有MixpanelClass的实例。这表明像这样的类定义

class MixpanelClass:
    def __init__(self, mp_id):
        self.mp_id = mp_id
        self.mp = Mixpanel(mp_id)

    def mp_track_event(self, user_id, event, property=None):
        self.mp.track(user_id, event, property)

    def mp_people_set(self, user_id, property):
        self.mp.people_set(user_id, property)

    def mp_people_increment(self, user_id, property):
        self.mp.people_increment(user_id, property)

    def mp_people_track_charge(self, user_id, revenue, property):
        self.mp.people_track_charge(user_id, revenue, property)

    def mp_get_distinct_id(self, request):
        mp_cookie_name = 'mp_' + self.mp_id + '_mixpanel'
        mp_cookie = request.COOKIES.get(mp_cookie_name, None)
        if mp_cookie:
            unquoted = unquote(mp_cookie)
            dictionary = literal_eval(unquoted)
            return dictionary.get('distinct_id')

现在,仅当您实际上具有 ID时,才实例化MixpanelClass

if settings.MIXPANEL_ID:
    m = MixpanelClass(settings.MIXPANEL_ID)