开发者

Should I use metaclasses here?

I'm reading some data, which I want to create classes out of - this is done at load-time. The classes are grouped into a Special class, which can only be instantiated using run-time information. The classes depend on this Special class, so they can only be useful once it is created. Here is some simplified code that shows how I want it to work, using random instead of actual run-time info:

import random
def make_foo(param1, param2):
    class Foo:
        def __init__(self, special):
            self.param1 = param1
            self.param2 = param开发者_C百科2
            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()

Sample output:

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

It can be summarized as having to create a set of classes which need to be bound to a special argument (so all the constructors, once the classes are done, just take a special argument). Can this be done more elegantly using meta-classes, or is this code fine the way it is?


Since I generally prefer classes of closures in Python, I'd use factory classes here and avoid the use of dynamically created classes alltogether. Example:

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)

An alternative to FooFactory would be the use of functools.partial(). If Foo is defined as above, you could do

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

and create instances of Foo by using

FooSpecialised(special)


This is not strictly an answer (or maybe it is) but I think this quote (MarkLutz, learning python 4 ed) of a quote from Tim Peters could be interesting:

To borrow a quote from the comp.lang.python newsgroup by veteran Python core developer Tim Peters (who is also the author of the famous “import this” Python motto):

[Metaclasses] are deeper magic than 99% of users should ever worry about. If you wonder whether you need them, you don’t (the people who actually need them know with certainty that they need them, and don’t need an explanation about why).


I ended up making a base class:

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

Foo et. al. are a normal-looking non-dynamic classes now:

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)

and they are used as follows:

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])

this minimizes code repetition (don't have to make tons of factory classes) and I also like the lingo much more than class factory.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜