Как сделать Obj-C Категории в Python? - PullRequest
9 голосов
/ 20 августа 2009

Obj-C (который я не использовал долгое время) имеет нечто, называемое category для расширения классов. Объявляя категорию с новыми методами и компилируя ее в вашу программу, все экземпляры класса неожиданно получают новые методы.

Python имеет возможности mixin, которые я использую, но mixins нужно использовать снизу программы: класс должен объявить его сам.

Предусмотренный вариант использования категории: скажем, у вас есть большая иерархия классов, которая описывает различные способы взаимодействия с данными, объявляя полиморфные способы получения различных атрибутов. Теперь категория может помочь потребителю этих описывающих классов, реализовав удобный интерфейс для доступа к этим методам в одном месте. (Метод категории может, например, попробовать два разных метода и вернуть первое определенное (не None) возвращаемое значение.)

Есть ли способ сделать это в Python?

Иллюстративный код

Я надеюсь это проясняет то, что я имею в виду. Дело в том, что категория похожа на агрегатный интерфейс, и потребитель AppObj может изменить его в его коде.

class AppObj (object):
  """This is the top of a big hierarchy of subclasses that describe different data"""
  def get_resource_name(self):
    pass
  def get_resource_location(self):
    pass

# dreaming up class decorator syntax
@category(AppObj)
class AppObjCategory (object):
  """this is a category on AppObj, not a subclass"""
  def get_resource(self):
    name = self.get_resource_name()
    if name:
      return library.load_resource_name(name)
    else:
      return library.load_resource(self.get_resource_location())

Ответы [ 3 ]

9 голосов
/ 20 августа 2009

Почему бы просто не добавить методы динамически?

>>> class Foo(object):
>>>     pass
>>> def newmethod(instance):
>>>     print 'Called:', instance
...
>>> Foo.newmethod = newmethod
>>> f = Foo()
>>> f.newmethod()
Called: <__main__.Foo object at 0xb7c54e0c>

Я знаю Objective-C, и это выглядит как категории. Единственным недостатком является то, что вы не можете сделать это для встроенных или расширенных типов.

4 голосов
/ 20 августа 2009

Я придумал эту реализацию декоратора класса. Я использую python2.5, поэтому я на самом деле не тестировал его с синтаксисом декоратора (что было бы неплохо), и я не уверен, что это действительно правильно. Но это выглядит так:

pycategories.py

"""
This module implements Obj-C-style categories for classes for Python

Copyright 2009 Ulrik Sverdrup <ulrik.sverdrup@gmail.com>
License: Public domain
"""

def Category(toclass, clobber=False):
    """Return a class decorator that implements the decorated class'
    methods as a Category on the class @toclass

    if @clobber is not allowed, AttributeError will be raised when
    the decorated class already contains the same attribute.
    """
    def decorator(cls):
        skip = set(("__dict__", "__module__", "__weakref__", "__doc__"))
        for attr in cls.__dict__:
            if attr in toclass.__dict__:
                if attr in skip:
                    continue
                if not clobber:
                    raise AttributeError("Category cannot override %s" % attr)
            setattr(toclass, attr, cls.__dict__[attr])
        return cls
    return decorator
1 голос
/ 26 ноября 2012

Функция Python setattr делает это простым.

# categories.py

class category(object):
    def __init__(self, mainModule, override = True):
        self.mainModule = mainModule
        self.override = override

    def __call__(self, function):
        if self.override or function.__name__ not in dir(self.mainModule):
            setattr(self.mainModule, function.__name__, function)

# categories_test.py

import this
from categories import category

@category(this)
def all():
    print "all things are this"

this.all()
>>> all things are this
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...