Игра с созданием объекта - PullRequest
1 голос
/ 01 ноября 2011

У меня есть два класса с методом foo:

Foo = type('Foo', (object,), {'foo': lambda s: 'Foo method'})
Bar = type('Bar', (object,), {'foo': lambda s: 'Bar method'})

И у меня есть другой класс, который мне нужно разделить на подклассы одного из перечисленных выше классов в соответствии с параметром.

Мое решение:

class Subject(object):
    def __new__(cls, key):
        base = (Foo if key else Bar)

        name = cls.__name__ + base.__name__
        dict_ = dict(cls.__dict__.items() + base.__dict__.items())
        bases = (base, cls)

        t = type(name, bases, dict_)
        return base.__new__(t)

    def bar(self):
        return 'Subject method'

Тестирование:

print(Subject(True).foo(), Subject(True).bar())
print(Subject(False).foo(), Subject(False).bar())

Выход:

('Foo method', 'Subject method')
('Bar method', 'Subject method')

Достаточно ли безопасно это решение? Или мне нужно что-то еще узнать? Есть ли еще какой-нибудь питонический способ делать такие нерегулярные вещи?

Ответы [ 3 ]

2 голосов
/ 01 ноября 2011

Я думаю, что большинство людей увидят приведенный выше код и рекомендуют использовать композицию, а не наследование. Субъект будет определять метод foo , который отправляется в правильный класс на основе логического значения.

В качестве альтернативы, вы можете использовать фабричную функцию для создания Foo или Bar по мере необходимости.

def subject(selector):
     'Factory function that chooses between Foo and Bar'
     return Foo() if selector else Bar()

При необходимости сделайте так, чтобы Foo и Bar наследовали от общего класса, чтобы функция фабрики всегда возвращала экземпляр подкласса общего класса.

1 голос
/ 01 ноября 2011

Если вы избегаете метаклассов (магии), код будет более читабельным (и, следовательно, более питоническим). Придерживайтесь подходов, предложенных Крисом и Рэймондом, т. Е.

использовать состав:

class Subject(object):
    def __init__(self, key):
        self.foo = (Foo if key else Bar)().foo
    def bar(self):
        return 'Subject method'

или используйте заводскую функцию:

def Subject(key):
    class Subject(Foo if key else Bar):
        def bar(self):
            return 'Subject method'
    return Subject()
0 голосов
/ 01 ноября 2011

Вот пример некоторого неожиданного поведения, которое может быть вызвано вашим текущим методом, даже если Subject переопределяет что-то из Foo или Bar, версия метода Foo или Bar будетПозвонил:

class Subject(object):
    # all of your current methods as they were above
    def foo(self):
        return 'Subject foo'

>>> Subject(False).foo()
'Bar method'

Даже если бы вы могли это исправить, поместив base.__dict__.items() перед cls.__dict__.items() на строку, где вы создаете dict_, я бы предложил полностью отказаться от этого подхода, возможно, используя Криса Лутца.'comment or Raymond's answer .

Если все, что вас интересует, это динамический метод, я бы предложил следующее.Создайте приватные версии методов внутри класса Subject, а затем назначьте метод, который вы хотите использовать в Subject.__init__():

class Subject(object):
    def __init__(self, key):
        self.foo = self._Foo_foo if key else self._Bar_foo
    def _Foo_foo(self):
        return 'Foo method'
    def _Bar_foo(self):
        return 'Bar method'

>>> Subject(True).foo()
'Foo method'
>>> Subject(False).foo()
'Bar method'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...