如何动态构造方法?

2024-06-16 10:29:31 发布

您现在位置:Python中文网/ 问答频道 /正文

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

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列表很长,因此如何以简洁的方式获得此行为?也许完全是另一种设计?你知道吗


Tags: 实例方法self标准属性foodefbar
2条回答

您正在寻找一种动态构造一组方法的方法。这通常不是一个好主意,但有时确实是。(例如,考虑像PyObjC和pythoncom这样的库,它们为ObjC和COM类构建动态代理,而这些类直到运行时才知道)。你还能怎么做呢?)你知道吗

所以,你应该仔细考虑你是否真的想要和需要这个,但是,如果你真的想要,有两种基本的方法。你知道吗

动态构建静态类

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

唯一棘手的一点是不能只写bar.f1 = …,因为f1只能作为字符串使用。所以我们必须用^{}来做:

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

静态构建动态类

如果您试图包装任何满足一些基本条件的内容,而不是某个特定类的特定方法列表,那么在有人尝试调用这些方法之前,您将不知道要包装什么,或者如何包装。因此,您必须捕获查找未知方法的尝试,并动态构建包装器。为此,我们重写^{}

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,然后在f上调用__get__,并返回结果。(如果你不知道这是什么意思,你就不想写这部分……)

您可以实现__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

相关问题 更多 >