如何动态构造方法?

时间:2018-05-25 22:39:25

标签: python class oop

我设计了一个班级。它非常标准,有一些方法属性

class foo:
   def f1(self):
      print 'f1'
   def f2(self):
      print 'f2'
   ....
   def fn(self):
      print 'fn'

现在我想创建一个包含一组foo实例的类。

class bar:
  self.myfoos=[foo(),foo(),foo()]

然后我想在所有foo实例上对f1..fn方法进行分类。 我能做到:

   class bar:
   ...
   def f1():
     for foo_ in self.myfoos:
        foo_.f1()

但是,我的f1..fn列表很长,所以我怎么能以一种简单的方式获得这种行为?可能完全采用其他设计?

2 个答案:

答案 0 :(得分:1)

您正在寻找一种动态构建一堆方法的方法。这经常不是一个好主意 - 但有时却是。 (例如,考虑像PyObjC和pythoncom这样的库构建ObjC和COM类的动态代理,直到运行时你才知道它们。你怎么能这样做?)

所以,你应该仔细考虑一下你是否真的想要并且需要这个 - 但是,如果你这样做,有两种基本方法。

动态构建静态类

如果您只是尝试包装foo个对象的集合,则可以在循环中创建所有方法。方法并不是太神奇;你只需将它们定义为与任何其他函数相同,并将它们分配给类。

唯一棘手的问题是你不能只写bar.f1 = …,因为f1只能作为字符串使用。所以我们必须使用setattr来执行此操作:

class bar:
    # your existing stuff
for name in 'f1 f2 f3 f4 f5 f6 f7 f8'.split():
    foometh = getattr(foo, name)
    def f(self):
        for foo in self.myfoos:
            foometh(foo)
    f.__name__ = name
    setattr(bar, name, f)

如果某种规则指定了您要转发的方法,而不是一堆方法名称列表,那么您可以执行以下操作:

for name, foometh in inspect.getmembers(foo):
    if name.startswith('_') or not isinstance(foometh, types.FunctionType)) or <rest of your rule>:
        continue
    def f(self):
        # from here it's the same as above

静态构建动态类

如果您正在尝试完成符合某些基本资格的任何,而不是某些特定课程的某些特定方法列表,那么您就不会知道自己想要什么包装,或者你想如何包装它,直到有人试图调用这些方法。所以你必须抓住尝试查找未知方法,并动态构建包装器。为此,我们覆盖__getattr__

class bar:
    # your existing stuff
    def __getattr__(self, attr):
        if attr.startswith('_') or <other rules here>:
            raise AttributeError
        def f():
            for foo in self.myfoos:
                foometh(foo)
        f.__name__ = attr
        return f

如果你不要过于仔细地观察,那么这个版本会返回像绑定方法一样的函数,而不是可以被内省的实际绑定方法。如果您想要后者,请通过将self作为参数添加到f,然后在__get__上调用f并返回结果来显式绑定方法。 (如果你不知道这意味着什么,你就不想写这部分......)

答案 1 :(得分:1)

您可以实施__getattr__并将该调用委托给foos列表。我确信有更优雅的方法可以做到这一点:

class foo:
    def f1(self):
        print('f1')
    def f2(self):
        print('f2')

class bar:
    def __init__(self):
        self.foos = [foo() for _ in range(3)]
    def __getattr__(self, fn):
        def fns(*args, **kwargs):
            for f in self.foos:
                getattr(f, fn)(*args, **kwargs)
        return fns

In []:
b = bar()
b.f1()

Out[]
f1
f1
f1

In []:
b.f2()

Out[]:
f2
f2
f2