Должен ли я использовать метаклассы здесь? - PullRequest
0 голосов
/ 20 апреля 2011

Я читаю некоторые данные, из которых я хочу создать классы - это делается во время загрузки. Классы сгруппированы в класс Special, который может быть создан только с использованием информации времени выполнения. Классы зависят от этого Special класса, поэтому они могут быть полезны только после его создания. Вот некоторый упрощенный код, который показывает, как я хочу, чтобы он работал, используя random вместо фактической информации времени выполнения:

import random
def make_foo(param1, param2):
    class Foo:
        def __init__(self, special):
            self.param1 = param1
            self.param2 = param2
            self.special = special
        def do(self):
            print "%s is doing" % self
        def __str__(self):
            return "Foo<%s,%s with %s>" % (self.param1, self.param2,
                                           self.special)

    return Foo
def make_bar(foo):
    class Bar:
        def __init__(self, special):
            self.foo = foo(special)
        def do(self):
            print "%s is doing" % (self,)
        def __str__(self):
            return "Bar<%s>" % self.foo
    return Bar

def make_grouper(foobars):
    class Grouper:
        def __init__(self, special):
            self.foobars = [foobar(special) for foobar in foobars]
    return Grouper

def make_special(howtomake, groups):
    class Special:
        def __init__(self):
            self.important = random.choice(howtomake)
            self.groups = [group(self) for group in groups]
        def __str__(self):
            return "Special<%s>" % self.important
    return Special

Foo10_20 = make_foo(10, 20)
Foo30_40 = make_foo(30, 40)
Bar = make_bar(Foo10_20)
Grouper1 = make_grouper([Foo10_20, Foo30_40])
Grouper2 = make_grouper([Bar, Bar])

Special = make_special("IMPORTANTINFO", [Grouper1, Grouper2])

s = Special()
s.groups[0].foobars[0].do()
s.groups[0].foobars[1].do()
s.groups[1].foobars[0].do()

s = Special()
s.groups[0].foobars[0].do()
s.groups[0].foobars[1].do()
s.groups[1].foobars[0].do()

Пример вывода:

Foo<10,20 with Special<O>> is doing
Foo<30,40 with Special<O>> is doing
Bar<Foo<10,20 with Special<O>>> is doing
Foo<10,20 with Special<I>> is doing
Foo<30,40 with Special<I>> is doing
Bar<Foo<10,20 with Special<I>>> is doing

Это можно резюмировать как необходимость создания набора классов, которые должны быть связаны с аргументом special (поэтому все конструкторы, когда классы завершены, просто принимают аргумент special). Можно ли сделать это более элегантно, используя мета-классы, или этот код в порядке?

Ответы [ 3 ]

3 голосов
/ 20 апреля 2011

Поскольку я обычно предпочитаю классы замыканий в Python, я бы использовал здесь фабричные классы и полностью избегал использования динамически создаваемых классов.Пример:

class Foo:
    def __init__(self, param1, param2, special):
        self.param1 = param1
        self.param2 = param2
        self.special = special
    def do(self):
        print "%s is doing" % self
    def __str__(self):
        return "Foo<%s,%s with %s>" % (self.param1, self.param2,
                                       self.special)

class FooFactory:
    def __init__(self, param1, param2):
        self.param1 = param1
        self.param2 = param2
    def __call__(self, special):
        return Foo(self.param1, self.param2, special)

foo_factory = FooFactory(1, 2)
foo = foo_factory(3)

Альтернативой FooFactory будет использование functools.partial().Если Foo определено, как указано выше, вы можете сделать

FooSpecialised = functools.partial(Foo, param1, param2)

и создать экземпляры Foo, используя

FooSpecialised(special)
2 голосов
/ 20 апреля 2011

Это не совсем ответ (или, возможно, так оно и есть), но я думаю, что эта цитата (MarkLutz, изучение Python 4 ed) цитаты Тима Питерса может быть интересной:

Чтобы взять цитату из Группа новостей comp.lang.python от ветерана Разработчик ядра Python Тим Питерс (который также является автором известного «Импортируй это» девиз Python):

[Метаклассы] более глубокая магия, чем 99% пользователей должны когда-либо беспокоиться. Если вам интересно, нужны ли они вам, вы не (люди, которые на самом деле нужно, чтобы они знали с уверенностью, что они им нужны, и им не нужны объяснение почему).

0 голосов
/ 20 апреля 2011

В итоге я создал базовый класс:

class Base(object):
    @classmethod
    def bind_init(cls, *args, **kwargs):
        def init(special):
            return cls(special, *args, **kwargs)
        return init

Foo et. и др. теперь выглядят нормально динамические классы:

class Foo(Base):
    def __init__(self, special, param1, param2):
        self.param1 = param1
        self.param2 = param2
        self.special = special
    def do(self):
        print "%s is doing" % self
    def __str__(self):
        return "Foo<%s,%s with %s>" % (self.param1, self.param2,
                                       self.special)

и они используются следующим образом:

Foo10_20 = Foo.bind_init(10, 20)
Foo30_40 = Foo.bind_init(30, 40)
Bar = Bar.bind_init(Foo10_20)
Grouper1 = Grouper.bind_init([Foo10_20, Foo30_40])
Grouper2 = Grouper.bind_init([Bar, Bar])

Special = Special.bind_init("IMPORTANTINFO", [Grouper1, Grouper2])

это сводит к минимуму повторение кода (не нужно создавать тонны фабричных классов), и мне также нравится язык намного больше, чем фабрика классов.

...