【问题标题】:Is there a way to loop through and execute all of the functions in a Python class?有没有办法循环并执行 Python 类中的所有函数?
【发布时间】:2011-02-05 13:42:06
【问题描述】:

我有

class Foo():
    function bar():
        pass

    function foobar():
        pass

而不是像下面这样一个一个地执行每个函数:

x = Foo()
x.bar()
x.foobar()

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

【问题讨论】:

    标签: python reflection class


    【解决方案1】:
    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) 行触发了这种情况:

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

    assignOrder(1) 返回一个函数,该函数接受另一个函数,对其进行更改(添加字段order 并将其设置为1)并返回它。然后在它装饰的函数上调用这个函数(它的order 字段因此被设置);结果替换了原来的函数。

    这是一种更漂亮、更易读、更易于维护的说法:

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

    【讨论】:

    • 尽可能不要使用eval。这样的事情应该可以工作:[getattr(x, field) for field in dir(x) if hasattr(getattr(x, field), 'order')]
    • 我知道必须有更好的方法:)
    • 这几乎正是我想要的,但我遇到了一些麻烦。第 195 行,在装饰器中 evaldict = func.func_globals.copy() AttributeError: 'int' object has no attribute 'func_globals'
    • @user3467349 这似乎与我写的无关:/
    • @badp 当我在 Python 中 C/P 你的代码时什么都不会发生。但是,如果我在 assignOrder 函数中删除 [at]decorator,这将起作用!所以,例如这就是我现在使用的:def assign_order(order): def orders_decorator(to_func): to_func.__order__ = order return to_func return orders_decorator
    【解决方案2】:

    没有。您可以访问Foo.__dict__,并依次调用每个值(捕获不可调用成员的错误),但不保留顺序。

    for callable in Foo.__dict__.values():
        try:
            callable()    
        except TypeError:
            pass
    

    这假设所有函数都没有参数,就像你的例子一样。

    【讨论】:

      【解决方案3】:

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

      如果您不关心顺序,请使用班级的__dict__

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

      如果函数需要额外的参数,这也有效 - 只需将它们放在类的实例之后。

      【讨论】:

      • 如果我不关心订单有什么办法吗?
      【解决方案4】:

      只要你只对 Python 3.x 感兴趣(从你的类语句中的空括号我猜你可能会感兴趣),那么实际上有一个简单的方法可以在没有装饰器的情况下做到这一点:Python 3允许您在定义类时提供自己的类似字典的对象。

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

      # 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__')])
      

      【讨论】:

        【解决方案5】:

        可能有一种最短的方法(类名是C):

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

        filter 表达式返回 C 类的所有函数。

        OR 在一行中:

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

        您可以通过某种方式对函数进行排序,例如,通过稍微复杂一点的表达式按名称的字典顺序排列。

        【讨论】:

        • c = C(); [f(c,*arg) for f in C.__dict__.values() if callable(f)]
        【解决方案6】:

        这有效并保留了顺序:

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

        输出将是:

        10--> 10
        10--> 20
        10--> 30
        

        注意:使用 this 而不是 F.__dict__.values() 的好处是,您可以在此处列出您希望调用的那些函数,而不必是全部。

        【讨论】:

          【解决方案7】:

          我遇到了一个与您有关的问题,但可能会帮助其他人解决与我遇到的相同情况。

          我在尝试什么:

          我在 python 中学习 datetime 模块并创建了一个对象。现在,要从该对象中获取详细信息,我需要手动按 Tab 键,获取可用方法/属性列表,然后键入它们,最后在得到结果后。并且还需要重复其他时间!

          我需要什么:

          由于混合了方法(可调用)和属性(不能调用),我需要将它们全部自动化以使用或调用(se),并在一个循环中查看它们的结果。

          我尝试了什么:

          我刚刚做了一个简单的 for 循环,它遍历 dir(datetimeobj)... 让我告诉你如何...

          # Creating an Obj
          date = datetime.datetime(2020,1,29,12,5)
          
          # Printing (But won't work, discussing 'why' in a bit)
          for method in dir(date):
              if callable(method):
                  print(f"{method}() -->  {date.method()}")
              else:
                  print(f"{method}   -->  {date.method}")
          
          

          看起来不错,对吧!它应该工作......但是,不 - 它不会。 执行这段代码会报错...

          ##Error: 'datetime.datetime' 对象没有属性'method'


          问题:

          啊哈!我们正在尝试调用 method 这是字符串,所以 date.method() / date.method 无效。

          走向解决方案:

          我会尽量不在这里讨论这么长时间...因为给出的代码是不言自明的,但请看这个...

          dir_without_dunders = [method for method in dir(date) if not method.startswith('_')]
          
          for method in dir_without_dunders:
              RealMethod = eval(f'date.{method}')
              try:
                  if callable(RealMethod):
                      print(f"{method}() -->  {RealMethod()}")
                  else:
                      print(f"{method} --> {RealMethod}")
              except:
                  continue
          

          代码摘要:
          • 为您创建dir_without_dunders...
          • 使用另一个变量RealMethod,因为method 将使用字符串(名称)只是为了使打印更加清晰和合理
          • 主要的解决方案是eval(f'date.{method}')这就是hack
          • 为什么使用try 块? - 因为不是所有的方法都可以不带参数调用,而且它们需要不同的参数集合和参数数量,所以我只调用了那些可以简单调用的方法!

          就是这样。 适用于此日期时间对象调用,因为它的大多数方法具有空参数集。

          【讨论】:

            猜你喜欢
            • 2019-07-11
            • 2021-08-31
            • 2020-10-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多