Есть ли способ перебрать и выполнить все функции в классе Python? - PullRequest
8 голосов
/ 08 апреля 2010

у меня

class Foo():
    function bar():
        pass

    function foobar():
        pass

Вместо того, чтобы выполнять каждую функцию по очереди следующим образом:

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

существует ли встроенный способ циклического прохождения и выполнения каждой функции в той последовательности, в которой они написаны в классе?

Ответы [ 6 ]

9 голосов
/ 08 апреля 2010
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
8 голосов
/ 08 апреля 2010

Нет. Вы можете получить доступ к Foo.__dict__ и вызывать каждое значение по очереди (отлавливать ошибки для не вызываемых элементов), но порядок не сохраняется.

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

Предполагается, что ни одна из функций не принимает параметры, как в вашем примере.

5 голосов
/ 08 апреля 2010

Поскольку Python хранит методы (и другие атрибуты) класса в словаре, который принципиально неупорядочен, это невозможно.

Если вам не нужен порядок, используйте класс __dict__:

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

Это также работает, если функция принимает дополнительные параметры - просто поместите их после экземпляра класса.

1 голос
/ 21 июля 2016

Вероятно, существует один из самых коротких методов (имя класса 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.

ИЛИ в одну строку:

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

Вы можете как-то упорядочить функции, например, по лексикографическому порядку их имен по чуть более сложному выражению.

1 голос
/ 09 апреля 2010

Пока вас интересует только 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__')])
0 голосов
/ 08 февраля 2019

Это работает и сохраняет порядок:

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() здесь у вас может быть список тех функций, которые вы предпочитаете вызывать, и не обязательно все из них.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...