我应该在这里使用元类吗?

时间:2011-04-20 15:02:36

标签: python metaprogramming metaclass

我正在阅读一些我希望创建类的数据 - 这是在加载时完成的。这些类被分组为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 个答案:

答案 0 :(得分:3)

由于我通常更喜欢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)

答案 1 :(得分:2)

这不是一个严格的答案(或者可能是),但我认为蒂姆·彼得斯引用的引用(MarkLutz,学习python 4 ed)可能很有趣:

  

从中借用一句话   由退伍军人编写的comp.lang.python新闻组   Python核心开发人员Tim Peters(谁   也是着名的作者   “导入这个”Python座右铭):

     

[Metaclasses]比魔法更深刻   99%的用户应该担心。   如果你想知道你是否需要它们,   你没有(实际的人   需要他们肯定地知道   他们需要它们,而且不需要它们   解释为什么)。

答案 2 :(得分:0)

我最终创建了一个基类:

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

这可以最大限度地减少代码重复(不必制作大量的工厂类),而且我也比工厂更喜欢这种语言。