关于反射:有没有办法循环并执行Python类中的所有函数?

Is there a way to loop through and execute all of the functions in a Python class?

我有

1
2
3
4
5
6
class Foo():
    function bar():
        pass

    function foobar():
        pass

而不是一个接一个地执行每个函数,如下所示:

1
2
3
x = Foo()
x.bar()
x.foobar()

是否有一种内置的方法来循环并按照类中写入函数的顺序执行每个函数?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def assignOrder(order):
  @decorator
  def do_assignment(to_func):
    to_func.order = order
    return to_func
  return do_assignment

class Foo():

  @assignOrder(1)
  def bar(self):
    print"bar"

  @assignOrder(2)
  def foo(self):
    print"foo"

  #don't decorate functions you don't want called
  def __init__(self):
    #don't call this one either!
    self.egg = 2

x = Foo()
functions = sorted(
             #get a list of fields that have the order set
             [
               getattr(x, field) for field in dir(x)
               if hasattr(getattr(x, field),"order")
             ],
             #sort them by their order
             key = (lambda field: field.order)
            )
for func in functions:
  func()

def bar(self)上方有趣的@assignOrder(1)线引发了这种情况:

1
Foo.bar = assignOrder(1)(Foo.bar)

assignOrder(1)返回一个接受另一个函数的函数,更改它(添加字段order并将其设置为1并返回它。然后对它所修饰的函数调用这个函数(这样设置了它的order字段);结果将替换原来的函数。

这是一种更为花哨、更易读、更易于维护的表达方式:

1
2
3
  def bar(self):
    print"bar"
  Foo.bar.order = 1


不可以,您可以访问Foo.__dict__,然后依次调用每个值(捕捉不可调用成员的错误),但顺序不被保留。

1
2
3
4
5
for callable in Foo.__dict__.values():
    try:
        callable()    
    except TypeError:
        pass

这假定没有任何函数采用参数,如您的示例中所示。


因为Python将类的方法(和其他属性)存储在字典中,而字典基本上是无序的,所以这是不可能的。

如果您不关心订单,请使用类的__dict__

1
2
3
4
5
x = Foo()
results = []
for name, method in Foo.__dict__.iteritems():
    if callable(method):
        results.append(method(x))

如果函数接受了额外的参数,这也可以工作——只需将它们放在类的实例之后。


可能有一个最短的方法(类名是c):

1
2
for func in filter(lambda x: callable(x), C.__dict__.values()):
    pass # here func is the next function, you can execute it here

筛选器表达式返回类C的所有函数。

或者在一行中:

1
[func() for func in filter(lambda x: callable(x), C.__dict__.values())]

您可以通过某种方式对函数进行排序,例如,通过对函数名称的词典编排顺序,使用稍微复杂一些的表达式。


只要您只对python 3.x感兴趣(从类语句中的空括号中,我猜您可能对它感兴趣),那么实际上有一种简单的方法可以做到这一点而不需要修饰:python3允许您在定义类时提供自己的类似字典的对象。

以下代码来自PEP3115,除了最后几行,我添加了这几行来按顺序打印方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# The custom dictionary
class member_table(dict):
  def __init__(self):
     self.member_names = []

  def __setitem__(self, key, value):
     # if the key is not already defined, add to the
     # list of keys.
     if key not in self:
        self.member_names.append(key)

     # Call superclass
     dict.__setitem__(self, key, value)

# The metaclass
class OrderedClass(type):

   # The prepare function
   @classmethod
   def __prepare__(metacls, name, bases): # No keywords in this case
      return member_table()

   # The metaclass invocation
   def __new__(cls, name, bases, classdict):
      # Note that we replace the classdict with a regular
      # dict before passing it to the superclass, so that we
      # don't continue to record member names after the class
      # has been created.
      result = type.__new__(cls, name, bases, dict(classdict))
      result.member_names = classdict.member_names
      return result

class MyClass(metaclass=OrderedClass):
  # method1 goes in array element 0
  def method1(self):
     pass

  # method2 goes in array element 1
  def method2(self):
     pass

x = MyClass()
print([name for name in x.member_names if hasattr(getattr(x, name), '__call__')])


这起作用并保持了秩序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class F:

    def f1(self, a):
        return (a * 1)

    def f2(self, a):
        return (a * 2)

    def f3(self, a):
        return (a * 3)

    allFuncs = [f1, f2, f3]

def main():
    myF = F()
    a = 10
    for f in myF.allFuncs:
        print('{0}--> {1}'.format(a, f(myF, a)))

输出将是:

1
2
3
10--> 10
10--> 20
10--> 30

注意:使用这个函数而不是F.__dict__.values()的好处是,这里可以列出您希望调用的那些函数,但不一定全部调用。