Как потребовать реализацию метода в Python? - PullRequest
1 голос
/ 24 сентября 2010

Я использую Duck Typing в Python.

def flagItem(object_to_flag, account_flagging, flag_type, is_flagged):
    if flag_type == Flags.OFFENSIVE:
        object_to_flag.is_offensive=is_flagged
    elif flag_type == Flags.SPAM:
        object_to_flag.is_spam=is_flagged
    object_to_flag.is_active=(not is_flagged)
    object_to_flag.cleanup()
    return object_to_flag.put()

Где различные объекты передаются как object_to_flag, каждый из которых имеет атрибуты is_active, is_spam, is_offensive. У них также есть метод cleanup().

Все объекты, которые я передаю, имеют один и тот же базовый класс (они являются объектами db в Google App Engine):

class User(db.Model):
    ...
    is_active = db.BooleanProperty(default = True)
    is_spam = db.BooleanProperty(default=False)
    is_offensive = db.BooleanProperty(default=False)
    def cleanup():
        pass

class Post(db.Model):
    ...
    is_active = db.BooleanProperty(default = True)
    is_spam = db.BooleanProperty(default=False)
    is_offensive = db.BooleanProperty(default=False)
    def cleanup():
        pass

Как я могу сделать метод cleanup() абстрактным, чтобы у меня мог быть один и тот же родительский класс для всех этих объектов, который требует, чтобы дочерние элементы обеспечивали реализацию?

Возможно, что еще важнее, это «питон»? Должен ли я идти по этому пути, или я просто должен полагаться на утку печатать? Я имею опыт работы с Java и пытаюсь научиться работать с Python.

Спасибо!

Ответы [ 5 ]

4 голосов
/ 24 сентября 2010

Используйте модуль abc . В частности, установите метакласс вашего базового класса на ABCMeta и используйте декоратор @abstractmethod в вашем методе cleanup.

Спор о том, является ли это "питоническим", разделен. PEP 3119 , который описывает стандарт, перечисляет некоторые плюсы и минусы (но, очевидно, предпочитает азбуку) Он попал в стандартную библиотеку, что является довольно хорошим показателем того, что многие люди считают его полезным в некоторых обстоятельствах. В твоем случае я думаю, что это уместно.

1 голос
/ 24 сентября 2010

Почему бы не сделать метод cleanup просто поднять NotImplementedError при его вызове?Если они хотят, чтобы уроки ваших детей работали, им придется внедрить какую-то реализацию.

1 голос
/ 24 сентября 2010

Если вы хотите убедиться, что метод очистки реализован, вы можете обернуть его декоратором @abc.virtualmethod.Это приведет к ошибке при создании экземпляра любого объекта, который не переопределил виртуальный метод.Это также требует, чтобы вы сделали abc.ABCMeta * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 'обычно будут только документы, подтверждающие, что разработчики должны переопределить данный метод.Однако это может быть связано скорее с новизной модуля abc (новинка в Python 2.6), чем с воспринимаемой непитоничностью этого подхода.

0 голосов
/ 25 сентября 2010

Хотя я никогда не использовал его лично, я видел много ссылок на интерфейсы Zope . Это может быть излишним для вашей задачи, но это может дать вам некоторое направление. И это может быть удобно для кого-то с фоном Java.

0 голосов
/ 24 сентября 2010

Поскольку у вас нет abc, вы можете сделать это с помощью простого метакласса

class Abstract(type(db.Model)):
    def __new__(metacls, clsname, bases, clsdict):
        for methodname in clsdict.pop('_abstract_methods_', []):
            try:
                if not callable(clsdict[methodname]):
                    raise TypeError("{0} must implement {1}".format(clcname, methodname))
            except KeyError:
                raise TypeError("{0} must implement {1}".format(clcname, methodname))
       return super(Abstract, metacls).__new__(metacls, clsname, bases, clsdict)


class RequireCleanup(db.Model):
    __metaclass__ = Abstract
    _abstract_methods_ = ['cleanup']

    def cleanup(self):
        pass

, выражение type(db.Model) разрешает любой метакласс, используемый для db.Model, поэтому мы не выполняем шагна код Google.Кроме того, мы извлекаем _abstract_methods_ из словаря классов, прежде чем он будет передан в метод Google __new__, чтобы мы ничего не сломали.Если у db.Model уже есть атрибут с таким именем, вам нужно изменить его на что-то другое.

...