Como obtenho uma lista de métodos em uma classe Python?

331

Eu quero percorrer os métodos em uma classe ou manipular objetos de classe ou instância de maneira diferente, com base nos métodos presentes. Como obtenho uma lista de métodos de classe?

Veja também:

Purrell
fonte
1
A fonte ..... a fonte ...
RickyA
4
@ JonCrowell, não é assim que a Força funciona.
Ken Williams
Para Cython, usando a diretiva compilador bindingfunciona: stackoverflow.com/a/46041480/1959808
Ioannis Filippidis
4
Em python3: [f for f in dir(ClassName) if not f.startswith('_')]ou apenas dir(ClassName)para tudo
Seraf
@Seraf Por favor, não poste respostas nos comentários. Poste uma resposta.
wjandrea 11/04

Respostas:

332

Um exemplo (listando os métodos da optparse.OptionParserclasse):

>>> from optparse import OptionParser
>>> import inspect
#python2
>>> inspect.getmembers(OptionParser, predicate=inspect.ismethod)
[([('__init__', <unbound method OptionParser.__init__>),
...
 ('add_option', <unbound method OptionParser.add_option>),
 ('add_option_group', <unbound method OptionParser.add_option_group>),
 ('add_options', <unbound method OptionParser.add_options>),
 ('check_values', <unbound method OptionParser.check_values>),
 ('destroy', <unbound method OptionParser.destroy>),
 ('disable_interspersed_args',
  <unbound method OptionParser.disable_interspersed_args>),
 ('enable_interspersed_args',
  <unbound method OptionParser.enable_interspersed_args>),
 ('error', <unbound method OptionParser.error>),
 ('exit', <unbound method OptionParser.exit>),
 ('expand_prog_name', <unbound method OptionParser.expand_prog_name>),
 ...
 ]
# python3
>>> inspect.getmembers(OptionParser, predicate=inspect.isfunction)
...

Observe que getmembersretorna uma lista de 2 tuplas. O primeiro item é o nome do membro, o segundo item é o valor.

Você também pode passar uma instância para getmembers:

>>> parser = OptionParser()
>>> inspect.getmembers(parser, predicate=inspect.ismethod)
...
codeape
fonte
4
perfeito, a parte do predicado é fundamental, caso contrário, você obtém a mesma coisa que dict com as informações meta adicionais. Obrigado.
21415 Purrell
2
Isso produzirá uma lista de todos os métodos da classe (incluindo aqueles herdados de outras classes) ou apenas listará os métodos explicitamente definidos nessa classe?
Anderson Green
10
inspect.isroutinepode ser um predicado mais apropriado; inspect.ismethodnão funciona para os métodos de todos os objetos.
Gavin S. Yancey
1
Isso funcionou apenas ao usar uma instância da classe (como o exemplo também). Esse comportamento é esperado?
Christian Reall-Fluharty
2
no python 3.7 pelo menos isso não funciona, você precisa instanciar a classe
kederrac
222

Existe o dir(theobject) método para listar todos os campos e métodos do seu objeto (como uma tupla) e o módulo inspecionar (como codeape write) para listar os campos e métodos com seus documentos (em "" ").

Como tudo (inclusive campos) pode ser chamado em Python, não tenho certeza se existe uma função interna para listar apenas métodos. Você pode tentar se o objeto que você obtém diré passível de chamada ou não.

Vincent Demeester
fonte
3
dir (objeto) é a solução mais simples que já usei.
Lstodd
@skjerns São necessários 5 símbolos para digitá-lo. :)
Dr_Zaszuś
117

Resposta do Python 3.x sem bibliotecas externas

method_list = [func for func in dir(Foo) if callable(getattr(Foo, func))]

resultado excluído do dunder:

method_list = [func for func in dir(Foo) if callable(getattr(Foo, func)) and not func.startswith("__")]
Derorrist
fonte
38

Digamos que você queira conhecer todos os métodos associados à classe de lista Apenas digite O seguinte

 print (dir(list))

Acima fornecerá todos os métodos da classe list

Naresh Joshi
fonte
2
Esta é a melhor solução
Zeeshan Ahmad
3
print([ m for m in dir(my_class) if not m.startswith('__')])
Evhz
32

Experimente a propriedade __dict__.

Eugene Bulkin
fonte
16
Eu acho que você quer dizer ditado . Mas isso lista os atributos da instância, não os métodos.
me_and
1
... isso não funcionou para mim também. Depois de consultar a sintaxe do Markdown, acho que quero dizer __dict__.
me_and
3
@me_and você provavelmente está fazendo "self .__ dict__" ou, de forma mais genérica, chamando a versão da instância de __dict__. No entanto classes têm um __dict__muito e que deve exibir os métodos de classe :)
Seaux
21

você também pode importar o FunctionType dos tipos e testá-lo com o class.__dict__:

from types import FunctionType

class Foo:
    def bar(self): pass
    def baz(self): pass

def methods(cls):
    return [x for x, y in cls.__dict__.items() if type(y) == FunctionType]

methods(Foo)  # ['bar', 'baz']
Seaux
fonte
Isto funcionou bem para mim. Eu adicionei and not x.startswith('_')ao final da lista a compreensão para o meu uso para ignorar __init__os métodos privados e.
Christopher Pearson
2
Você pode livrar-se do importde FunctionTypeusando um lambda descartável com type():type(lambda:0)
Tersosauros
isinstanceseria o melhor do que type(y) == FunctionTypeaqui.
Gloweye
13

Observe que você precisa considerar se deseja que os métodos das classes base herdadas (mas não substituídas) sejam incluídos no resultado. As operações dir()e inspect.getmembers()incluem métodos de classe base, mas o uso do __dict__atributo não.

satyagraha
fonte
3

Isso também funciona:

mymodule.py

def foo(x)
   return 'foo'
def bar()
   return 'bar'

Em outro arquivo

import inspect
import mymodule
method_list = [ func[0] for func in inspect.getmembers(mymodule, predicate=inspect.isroutine) if callable(getattr(mymodule, func[0])) ]

resultado:

['foo', 'bar']

Nos documentos python:

inspect.isroutine (objeto)

Return true if the object is a user-defined or built-in function or method.
Josh Dando
fonte
2
def find_defining_class(obj, meth_name):
    for ty in type(obj).mro():
        if meth_name in ty.__dict__:
            return ty

assim

print find_defining_class(car, 'speedometer') 

Think Python página 210

diamante de scott lewis
fonte
3
Recuo de 5? Como capitalizar palavras-chave? Espaçamento no estilo não-pep8?
Florrie
1
Nem uma vez que os nazis da convenção de codificação chegaram lá!
Rbennell
1
Como isso responde à pergunta?
Aran-Fey
2

Existe esta abordagem:

[getattr(obj, m) for m in dir(obj) if not m.startswith('__')]

Ao lidar com uma instância de classe , talvez seja melhor retornar uma lista com as referências de método em vez de apenas nomes¹. Se esse é seu objetivo, além de

  1. Usando não import
  2. Excluindo métodos privados (por exemplo __init__) da lista

Pode ser útil. Em poucas palavras, para uma classe como

class Ghost:
    def boo(self, who):
        return f'Who you gonna call? {who}'

Poderíamos verificar a recuperação da instância com

>>> g = Ghost()
>>> methods = [getattr(g, m) for m in dir(g) if not m.startswith('__')]
>>> print(methods)
[<bound method Ghost.boo of <__main__.Ghost object at ...>>]

Então você pode ligar imediatamente:

>>> for method in methods:
...     print(method('GHOSTBUSTERS'))
...
Who you gonna call? GHOSTBUSTERS

¹ Um caso de uso:

Eu usei isso para testes de unidade . Teve uma classe em que todos os métodos realizavam variações do mesmo processo - o que levava a testes demorados, cada um apenas um pouco diferente dos outros. DRY era um sonho distante.

Achei que deveria ter um único teste para todos os métodos, então fiz a iteração acima.

Embora eu tenha percebido que, em vez disso, deveria refatorar o código em si para ser compatível com DRY ... isso ainda pode servir uma alma nítida e aleatória no futuro.

Julio Cezar Silva
fonte
2

Eu apenas mantenho isso lá, porque as respostas mais bem avaliadas não são claras .

Este é um teste simples com classe não usual baseada no Enum.

# -*- coding: utf-8 -*-
import sys, inspect
from enum import Enum

class my_enum(Enum):
    """Enum base class my_enum"""
    M_ONE = -1
    ZERO = 0
    ONE = 1
    TWO = 2
    THREE = 3

    def is_natural(self):
            return (self.value > 0)
    def is_negative(self):
            return (self.value < 0)

def is_clean_name(name):
    return not name.startswith('_') and not name.endswith('_')
def clean_names(lst):
    return [ n for n in lst if is_clean_name(n) ]
def get_items(cls,lst):
    try:
            res = [ getattr(cls,n) for n in lst ]
    except Exception as e:
            res = (Exception, type(e), e)
            pass
    return res


print( sys.version )

dir_res = clean_names( dir(my_enum) )
inspect_res = clean_names( [ x[0] for x in inspect.getmembers(my_enum) ] )
dict_res = clean_names( my_enum.__dict__.keys() )

print( '## names ##' )
print( dir_res )
print( inspect_res )
print( dict_res )

print( '## items ##' )
print( get_items(my_enum,dir_res) )
print( get_items(my_enum,inspect_res) )
print( get_items(my_enum,dict_res) )

E isso são resultados de saída.

3.7.7 (default, Mar 10 2020, 13:18:53) 
[GCC 9.2.1 20200306]
## names ##
['M_ONE', 'ONE', 'THREE', 'TWO', 'ZERO']
['M_ONE', 'ONE', 'THREE', 'TWO', 'ZERO', 'name', 'value']
['is_natural', 'is_negative', 'M_ONE', 'ZERO', 'ONE', 'TWO', 'THREE']
## items ##
[<my_enum.M_ONE: -1>, <my_enum.ONE: 1>, <my_enum.THREE: 3>, <my_enum.TWO: 2>, <my_enum.ZERO: 0>]
(<class 'Exception'>, <class 'AttributeError'>, AttributeError('name'))
[<function my_enum.is_natural at 0xb78a1fa4>, <function my_enum.is_negative at 0xb78ae854>, <my_enum.M_ONE: -1>, <my_enum.ZERO: 0>, <my_enum.ONE: 1>, <my_enum.TWO: 2>, <my_enum.THREE: 3>]

Então, o que temos:

  • dir fornecer dados não completos
  • inspect.getmembers fornecer dados não completos e chaves internas que não são acessíveis com getattr()
  • __dict__.keys()forneça resultado completo e confiável

Por que os votos são tão errôneos? E onde eu estou errado? E onde outras pessoas erradas, que respondem, têm votos tão baixos?

imbearr
fonte
1
methods = [(func, getattr(o, func)) for func in dir(o) if callable(getattr(o, func))]

fornece uma lista idêntica à

methods = inspect.getmembers(o, predicate=inspect.ismethod)

faz.

Patrick B.
fonte
0

Se o seu método é um método "regular" e não um statimethod, classmethodetc.
Existe um pequeno truque que eu criei -

for k, v in your_class.__dict__.items():
    if "function" in str(v):
        print(k)

Isso pode ser estendido para outros tipos de métodos, alterando "função" na ifcondição correspondente.
Testado em python 2.7.

markroxor
fonte
1
Não sei por que isso foi rebaixado ... ele realmente resolveu o problema que eu estava tendo.
redspidermkv 25/02
0

Você pode listar todos os métodos em uma classe python usando o seguinte código

dir(className)

Isso retornará uma lista de todos os nomes dos métodos na classe

Ekene Oguikpu
fonte
-1

Eu sei que este é um post antigo, mas acabei de escrever esta função e a deixarei aqui, caso alguém tropece em busca de uma resposta:

def classMethods(the_class,class_only=False,instance_only=False,exclude_internal=True):

    def acceptMethod(tup):
        #internal function that analyzes the tuples returned by getmembers tup[1] is the 
        #actual member object
        is_method = inspect.ismethod(tup[1])
        if is_method:
            bound_to = tup[1].im_self
            internal = tup[1].im_func.func_name[:2] == '__' and tup[1].im_func.func_name[-2:] == '__'
            if internal and exclude_internal:
                include = False
            else:
                include = (bound_to == the_class and not instance_only) or (bound_to == None and not class_only)
        else:
            include = False
        return include
    #uses filter to return results according to internal function and arguments
    return filter(acceptMethod,inspect.getmembers(the_class))
user3569372
fonte
Este código não faz o que se destina como publicado. Dar class_only ou instance_only resultará em lista vazia.
Oz123
-2

Isto é apenas uma observação. "codificar" parece ser um método para objetos de string

str_1 = 'a'
str_1.encode('utf-8')
>>> b'a'

No entanto, se str1 for inspecionado para métodos, uma lista vazia será retornada

inspect.getmember(str_1, predicate=inspect.ismethod)
>>> []

Então, talvez eu esteja errado, mas o problema parece não ser simples.

José Crespo Barrios
fonte
1
'a'É um objecto, cujo tipo é str. Você pode ver isso executando type('a'). inspect.getmember()usa um parâmetro de tipo, então você precisa ligar inspect.getmember(str)para ver o que espera.
lhasson 19/02
-3
class CPerson:
    def __init__(self, age):
        self._age = age

    def run(self):
        pass

    @property
    def age(self): return self._age

    @staticmethod
    def my_static_method(): print("Life is short, you need Python")

    @classmethod
    def say(cls, msg): return msg


test_class = CPerson
# print(dir(test_class))  # list all the fields and methods of your object
print([(name, t) for name, t in test_class.__dict__.items() if type(t).__name__ == 'function' and not name.startswith('__')])
print([(name, t) for name, t in test_class.__dict__.items() if type(t).__name__ != 'function' and not name.startswith('__')])

resultado

[('run', <function CPerson.run at 0x0000000002AD3268>)]
[('age', <property object at 0x0000000002368688>), ('my_static_method', <staticmethod object at 0x0000000002ACBD68>), ('say', <classmethod object at 0x0000000002ACF0B8>)]
Carson
fonte
-4

Se você deseja listar apenas métodos de uma classe python

import numpy as np
print(np.random.__all__)
Rakesh Chaudhari
fonte
1
Isso é um módulo, não uma classe.
Aran-Fey
@ Aran-Fey É aplicado para todas as classes, tudo é existir em todas as classes
Rakesh Chaudhari
2
Não, não faz. Nem existe em todos os módulos .
`` #