Como chamar todos os métodos de uma classe?

Olá a todos.

Digamos que tenho:

class Myclass:

    def method1:

    def method2:

         .
         .
         .

    def methodn:

agora digamos que eu quero chamar todos os métodos da classe Myclass. Eu tenho que chamar de um por um…

Tem como chamar a classe Myclass, fazendo com que todos os métodos sejam chamados?

Valeu.

Isto não faz sentido, para que você necessitaria chamar todos os métodos de uma só vez?

Preguiça de chamar todos um por um.

É uma classe pequena, mas eu vou utilizar ela em um cenário que todos sempre serão requisitados.

Teoricamente, há uma maneira de fazer isso usando os mecanismos de “reflection” da linguagem de programação, porém eles variam muito de linguagem para linguagem, e eles não são a solução adequada nesse cenário em que você tem controle da classe que chama e da classe que vai ser chamada.

É bom repensar a organização dessas funções (e talvez nem utilizar classes), ou no mínimo criar uma função que chama todas as outras funções.

2 curtidas

Cria um metodo que chama todos os outros que não ele…

(mas tem aquele negócio que, se vc está tentando fazer algo que ninguém jamais imaginou, há grandes chances de vc estar usando do jeito errado)

2 curtidas

O problema de usar soluções como reflection, tal como apontado por @Capezotte é que na verdade você não quer chamar todos os métodos de uma classe. De uma olhada em todos os métodos da sua classe:

from typing import Callable

class Myclass:
  casa = '12'

  def method1():
    ...
  
  def method2():
    ...

  def methodn():
    ...

my_class = Myclass()
[func_name for func_name in dir(my_class) 
  if isinstance(getattr(my_class, func_name), Callable)]
['__class__',
 '__delattr__',
 '__dir__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 'method1',
 'method2',
 'methodn']

Vai depender do real problema, mas talvez você queira algo como:

  from typing import List, Callable, Optional

class MyClass:

  def __init__(self):
    self._all_executor_methods = [
      self.method1,
      self.method2,
      self.methodn                         
    ]

  def run(self, extra_methods: List[Callable] = []):
    results = []
    extended = [*extra_methods, *self._all_executor_methods]
    for method in extended:
      results.append(method())
    return results

  
  def method1(self):
    print('a')
  
  def method2(self):
    print('b')

  def methodn(self):
    print('c')

my_class = MyClass()
my_class.run()

Perceba que a ordem dos métodos pode importar.
Pode ser ainda que você esteja em busca de alguns conceitos do paradigma funcional:

from typing import Any, List, Callable, Optional

def method1() -> Callable:
  def _method1(executor: Executor) -> None:
    print('a')
  return _method1
  
def method2() -> Callable:
  def _method2(executor: Executor) -> None:
    print('b')
  return _method2

def methodn(a, b, c) -> Callable:
  def _methodn(executor: Executor) -> None:
    executor.math_result = a*b/c
    print(executor.math_result)
  return _methodn

class Executor:

  def run(self, *methods: List[Callable]) -> List[Any]:
    results = []
    for method in methods:
      results.append(method(self))
    return results

executor = Executor()
executor.run(
  method1(), 
  method2(),
  methodn(12,22,11)
)
executor.math_result

código no Google Colab
De bonus os exemplos fazem uso de type hinting, se você planeja programar algo relativamente grande, tipagem é indispensável.

2 curtidas

Em nenhum sentido, me parece lógico chamar todos de uma vez, ou você estaria subdividindo um único método em vários métodos.
Ainda que fosse isto, e por menor que seja a classe, você estaria sobrecarregando o sistema, perdendo performance. Usou o método o garbage collector pode vir e limpar a memória, se necessário for.

Em geral, este tipo de pergunta demonstra que tem uma coisa muito errada na modelagem do sistema.

O pacote inspect (interno do python) consegue diferenciar os métodos padrões da classe dos métodos que você criou, sendo assim, você pode usar isso a seu favor:

from inspect import ismethod, isfunction

class FooBar():
    @staticmethod
    def foo():
        return 'foo'

    def bar(self):
        return 'bar'

#cria o objeto a partir da classe
obj = FooBar()

#cria uma tupla com todos os métodos da classe
all_methods = (getattr(obj, mt) for mt in dir(obj))

#filtra apenas os métodos criados
valid_methods = (f for f in all_methods if (ismethod(f) or isfunction(f)))

#executa os métodos
for method in valid_methods:
    print(method())

A função ismethod retorna True sempre que o atributo fornecido seja um método “não padrão” de uma classe. A função isfunction retorna True sempre que o atributo fornecido seja uma função qualquer (incluindo funções definidas com a função lambda e m´todos estáticos).

Assim, o all_methods pega todos os atributos da classe e o valid_methods filtra esses métodos usando as duas funções que acabei de citar.

Caso você não tenha nenhum método estático para ser executado dessa forma, utilize apenas o ismethod como filtro e de forma análoga com o isfunction caso existam apenas métodos estáticos.

Entendo que existam utilidades para isso. É algo muito similar a o que o unittest faz, mas use com cuidado! É uma lógica extremamente passível de apresentar bugs.

Se você estiver fazendo alguma forma de testar seu código e por qualquer motivo não esteja usando o pytest ou o unittest, lembre-se de montar com cuidado a estrutura try except dentro do laço for que roda todos os métodos e lembre-se que se for ser utilizado para códigos de terceiros, esse tipo de lógica representa uma brecha de segurança assim como a função eval, então, cuidado!

Documetação do inspect: https://docs.python.org/3/library/inspect.html#module-inspect

1 curtida

Ainda assim não é uma boa ideia, O exemplo que dei é apenas um dos motivos pelo qual você não quer chamar todos os métodos da sua classe, ou chamá-los via bibliotecas que exploram reflection como é o caso de inspect. As coisas podem ficar especialmente mais complicadas quando há herança envolvida:

class Zorg:
    def zorg(self):
        return 'zorg'

class FooBar(Zorg):
    @staticmethod
    def foo(self):
        return 'foo'

    def bar(self):
        return 'bar'

Ou se você decide tornar estas operações propriedade da classe, para desfrutar de polimorfismo.

class FooBar: 
    ...
    def run_methods(self):
        ...

class Blub(FooBar):
    ...

obj = Blub()
obj.run_methods()

E a lista segue, incluindo métodos privados assim como métodos não projetados para serem executados independentemente (um método que lê/grava dados de um banco de dados).

No geral usar reflection para criar funcionalidade dentro do seu sistema terminam gerando forte acoplamento entre componentes do seu código, geralmente esse é um dos motivos pelo qual boa parte se resume a criação de ferramentas externas ao sistema, para inspecionar os componentes de um programa de forma isolada ou patching temporário de bugs.

Claro existem alguns aspectos mais inofensivos de reflection como verificação de tipagem, instância e tal, mas eu diria que, em Python, também costuma ser uma má ideia, quase sempre em Python é melhor pedir desculpa do que pedir permissão.

1 curtida

Sim, eu sei disso, tanto que frisei no final da postagem que é algo arriscado de se fazer tanto do ponto de vista de lógica quanto de segurança. Só respondi de forma objetiva o autor do tópico, como fazer o que ele quer no caso mais simples (sem herança) porque não sei exatamente o que ele pretende fazer.

E se ele estiver montando uma estrutura análoga ao unittest para executar num embarcado e receber a resposta via serial, por exemplo, onde ele pretende receber algo diferente de um raise no caso de uma falha ? Ele utilizaria uma classe só para agrupar os testes que deseja realizar.

Como não sei a finalidade na qual ele quer fazer isso, parti para uma resposta objetiva apenas apontando que é algo que pode gerar bugs e falhas de segurança em casos mais complexos. Mas concordo que é algo complicado de ser feito e que o ideal é montar um método da própria classe que executa os demais numa sequência, como você sugeriu, mas essa solução pode não resolver o problema dele, afinal, a pergunta foi direta e sem nenhuma contextualização.

1 curtida

Neste caso, como você apontou, o melhor é ir buscar uma biblioteca de testes de fato, particularmente para ambientes embarcados se for o caso, como TPT.

Isso não descarta o uso de pytest e similares, mesmo que seja algo envolvendo ambiente embarcado, ou trabalhar com uso de exceptions (reflection e afins estão ai para você fazer o devido mocking) . E não necessariamente pedir desculpas em vez de permissão significa que você vai receber um ‘raise’, ainda assim é melhor usar um try except, mesmo em sistemas embarcados ou envolvendo mesclagem com linguagens de nível mais baixo, você simplesmente trata a exception ou faz a devida tradução para o nível mais baixo.

Por exemplo:

import time
from functools import reduce

def permision_sum_int(a, b):
  if type(a) == int and type(b) == int:
     return a + b
  elif type(a) == int:
    return a
  elif type(b) == int:
    return b
  else: 
    return 0

a_list = list(range(1000000))
a_list.append(None)

start = time.time()
print(reduce(permision_sum_int, a_list, 0))
end = time.time()
print(f'tempo: {end - start}')

def forgiveness_sum_int(a, b):
  try:
    return a + b + 0
  except TypeError:
    try:
      return a + 0
    except TypeError:
      try:
        return b + 0
      except TypeError:
        return 0

start = time.time()
print(reduce(forgiveness_sum_int, a_list, 0))
end = time.time()
print(f'tempo: {end - start}')
499999500000
tempo: 0.35408973693847656
499999500000
tempo: 0.1497352123260498