有没有一种方法可以循环执行 Python 类中的所有函数?

10

我有

class Foo():
    function bar():
        pass

    function foobar():
        pass

与其按以下方式逐个执行每个函数:

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

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

7个回答

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

上面有趣的@assignOrder(1)行触发了以下操作:def bar(self)

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

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

这是一种更加高级、易读和易于维护的方式,相当于说:

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

2
如果可能的话,请尽量避免使用eval。类似这样的代码应该可以解决问题:[getattr(x, field) for field in dir(x) if hasattr(getattr(x, field), 'order')] - Samir Talwar
@user3467349 这似乎与我所写的无关:/ - badp
@decorator不是decorator模块的方法吗? 无论如何,我在Python 2.7中完全复制了代码。 hasattr(getattr(x,'foo'),"order") False - user3467349
这对我不起作用!这与Python 2.7兼容吗? - Ashkan
1
@badp 当我在Python中复制/粘贴您的代码时,什么也没有发生。但是,如果我在assignOrder函数中删除[at]decorator,它就可以工作!因此,例如,这就是我现在正在使用的:def assign_order(order): def orders_decorator(to_func): to_func.__order__ = order return to_func return orders_decorator - Ashkan
显示剩余2条评论

10

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

for callable in Foo.__dict__.values():
    try:
        callable()    
    except TypeError:
        pass
这假设所有函数都不带参数,就像你的例子一样。

5

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

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

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

这也适用于函数需要额外参数的情况 - 只需在类实例后面放置它们即可。

如果我不关心顺序,有没有办法做到这一点? - curious

1

这可能是最短的方法之一(类名为C):

for func in filter(lambda x: callable(x), C.__dict__.values()):
    pass # here func is the next function, you can execute it here
< p > filter 表达式返回类 C 的所有函数。< /p > < p > 或者一行代码: < /p >
[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)] - Leon Chang

1
只要您只关心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__')])

1

这个代码可以正常运行并保持顺序:

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

注意:使用这个而不是F.__dict__.values()的优点在于,你可以列出那些你更喜欢调用的函数列表,而不一定是全部。


0

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

我试过的:

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

我需要的是:

由于存在可调用的方法(Methods)和不可调用的属性(Attribute),我需要自动化所有这些内容,以便在单个循环中使用或调用它们并查看其结果。

我尝试的是:

我只是制作了一个简单的 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}")

看起来没问题,对吧!它应该可以运行……但是,不行——不会成功。

##错误:‘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块?-因为并非所有方法都可以在没有任何参数的情况下调用,它们需要不同的参数集和数量,因此我只调用了那些可以简单调用的方法!

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接