【问题标题】:Should I use metaclasses here?我应该在这里使用元类吗?
【发布时间】:2011-08-09 14:26:14
【问题描述】:

我正在读取一些数据,我想从中创建类 - 这是在加载时完成的。这些类被分组到一个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 参数)。这可以使用元类更优雅地完成吗,或者这段代码是否可以正常使用?

【问题讨论】:

    标签: python metaprogramming metaclass


    【解决方案1】:

    因为我通常更喜欢 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】:

      这不是严格的答案(或者可能是),但我认为 Tim Peters 的这句话(MarkLutz,learning python 4 ed)可能很有趣:

      借用一句话 comp.lang.python 新闻组由老手 Python 核心开发人员 Tim Peters(谁 也是著名的作者 “import this” Python 格言):

      [元类]比 99% 的用户应该担心过。 如果您想知道是否需要它们, 你没有(实际上的人 需要他们确定地知道 他们需要它们,并且不需要 解释为什么)。

      【讨论】:

      • 是的,我读到了,但没有理会它,但感谢您提出。我觉得我需要一些东西..如果有什么我正在做的事情的概念是错误的。但工厂的想法运作良好,我正在使用它
      【解决方案3】:

      我最终创建了一个基类:

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

      Foo 等。人。现在是看起来很正常的非动态类:

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

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

      【讨论】:

        猜你喜欢
        • 2014-04-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-02
        • 1970-01-01
        • 2018-10-11
        相关资源
        最近更新 更多