¿Cómo obtengo la lista de métodos en una clase de Python?

331

Quiero iterar a través de los métodos en una clase, o manejar objetos de clase o instancia de manera diferente en función de los métodos presentes. ¿Cómo obtengo una lista de métodos de clase?

Ver también:

Purrell
fuente
1
La fuente ..... la fuente ...
RickyA
44
@ JonCrowell no es así como funciona la Fuerza.
Ken Williams
Para Cython, la directiva del compilador bindingfunciona: stackoverflow.com/a/46041480/1959808
Ioannis Filippidis
44
En python3: [f for f in dir(ClassName) if not f.startswith('_')]o solo dir(ClassName)para todo
Seraf
@Seraf Por favor, no publique respuestas en los comentarios. Publica una respuesta en su lugar.
wjandrea

Respuestas:

333

Un ejemplo (enumerando los métodos de la optparse.OptionParserclase):

>>> 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 getmembersdevuelve una lista de 2 tuplas. El primer elemento es el nombre del miembro, el segundo elemento es el valor.

También puede pasar una instancia a getmembers:

>>> parser = OptionParser()
>>> inspect.getmembers(parser, predicate=inspect.ismethod)
...
codeape
fuente
44
perfecto, la parte del predicado es clave, de lo contrario, obtienes lo mismo que dict con la metainformación adicional. Gracias.
Purrell
2
¿Producirá esto una lista de todos los métodos de la clase (incluidos los que se heredan de otras clases), o solo enumerará los métodos que se definen explícitamente en esa clase?
Anderson Green
10
inspect.isroutinepodría ser un predicado más apropiado; inspect.ismethodno funciona para todos los métodos de los objetos.
Gavin S. Yancey
1
Esto solo funcionó cuando se usa una instancia de la clase (como también lo hace el ejemplo). ¿Es este comportamiento esperado?
Christian Reall-Fluharty
2
en Python 3.7 al menos esto no funciona, tienes que crear una instancia de la clase
kederrac
222

Existe el dir(theobject)método para enumerar todos los campos y métodos de su objeto (como una tupla) y el módulo de inspección (como escritura de codeape) para enumerar los campos y métodos con su documento (en "" ").

Debido a que todo (incluso los campos) podrían llamarse en Python, no estoy seguro de que haya una función incorporada para enumerar solo los métodos. Es posible que desee probar si el objeto que atraviesa dires invocable o no.

Vincent Demeester
fuente
3
dir (objeto) es la solución más simple que he usado.
lstodd
@skjerns Se necesitan 5 símbolos para escribirlo. :)
Dr_Zaszuś
117

Python 3.x responde sin bibliotecas externas

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

resultado excluido de dunder:

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

Supongamos que desea conocer todos los métodos asociados con la clase de lista Solo escriba lo siguiente

 print (dir(list))

Lo anterior le dará todos los métodos de la clase de lista

Naresh Joshi
fuente
2
Esta es la mejor solución
Zeeshan Ahmad el
3
print([ m for m in dir(my_class) if not m.startswith('__')])
Evhz
32

Prueba la propiedad __dict__.

Eugene Bulkin
fuente
16
Creo que te refieres a dict . Pero eso enumera los atributos de la instancia, no los métodos.
me_y el
1
... eso tampoco funcionó para mí. Después de consultar la sintaxis de Markdown, creo que me refiero a __dict__.
me_y el
3
@me_y probablemente estés haciendo "self .__ dict__" o, más genéricamente, llamando a la versión de instancia de __dict__. Sin embargo, las clases también tienen __dict__y eso debería mostrar los métodos de clase :)
Seaux
21

También puede importar el FunctionType de los tipos y probarlo con 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
fuente
Esto funcionó bien para mí. Agregué and not x.startswith('_')al final de la lista la comprensión de mi uso para ignorar __init__los métodos privados y.
Christopher Pearson
2
Usted puede deshacerse de la importde FunctionTypemediante el uso de un lambda de usar y tirar con type():type(lambda:0)
Tersosauros
isinstanceSería mejor que type(y) == FunctionTypeaquí.
Gloweye
13

Tenga en cuenta que debe considerar si desea que los métodos de las clases base que se heredan (pero no se reemplacen) se incluyan en el resultado. Las operaciones dir()y inspect.getmembers()incluyen métodos de clase base, pero el uso del __dict__atributo no.

satyagraha
fuente
3

Esto también funciona:

mymodule.py

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

En otro archivo

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

salida:

['foo', 'bar']

De los documentos de Python:

inspect.isroutine (objeto)

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

Entonces

print find_defining_class(car, 'speedometer') 

Think Python página 210

Lewis Scott Diamond
fuente
3
Sangría de 5? ¿Capitalizar palabras clave? ¿Espaciado sin estilo pep8?
Florrie
1
¡Ni una vez que los nazis de la convención de codificación llegaron!
rbennell
1
¿Cómo responde esto a la pregunta?
Aran-Fey
2

Hay este enfoque:

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

Cuando se trata de una instancia de clase , quizás sería mejor devolver una lista con las referencias de métodos en lugar de solo nombres¹. Si ese es tu objetivo, así como

  1. Usando no import
  2. Excluir métodos privados (p __init__. Ej. ) De la lista

Puede ser de utilidad. En pocas palabras, para una clase como

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

Podríamos verificar la recuperación de la instancia con

>>> 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 ...>>]

Entonces puede llamarlo de inmediato:

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

¹ Un caso de uso:

Usé esto para pruebas unitarias . Tenía una clase en la que todos los métodos realizaban variaciones del mismo proceso, lo que conducía a largas pruebas, cada una a solo un toque de distancia de las demás. DRY era un sueño lejano.

Pensé que debería tener una sola prueba para todos los métodos, así que hice la iteración anterior.

Aunque me di cuenta de que en su lugar debería refactorizar el código en sí mismo para que sea compatible con DRY de todos modos ... esto aún puede servir a un alma puntiaguda al azar en el futuro.

Julio Cezar Silva
fuente
2

Simplemente mantengo esto allí, porque las respuestas mejor calificadas no son claras .

Esta es una prueba simple con una clase no habitual basada en 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) )

Y esto es resultados de salida.

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

Entonces lo que tenemos:

  • dir proporcionar datos no completos
  • inspect.getmembers proporcionar datos no completos y proporcionar claves internas a las que no se puede acceder con getattr()
  • __dict__.keys()proporcionar resultados completos y confiables

¿Por qué los votos son tan erróneos? ¿Y dónde me equivoco? ¿Y dónde equivocado otras personas cuyas respuestas tienen votos tan bajos?

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

da una lista idéntica a

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

hace.

Patrick B.
fuente
1

Puede enumerar todos los métodos en una clase de Python utilizando el siguiente código

dir(className)

Esto devolverá una lista de todos los nombres de los métodos en la clase

Ekene Oguikpu
fuente
0

Si su método es un método "regular" y no un método statimethod, classmethodetc.
Se me ocurrió un pequeño truco:

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

Esto puede extenderse a otro tipo de métodos cambiando la "función" en la ifcondición correspondiente.
Probado en python 2.7.

markroxor
fuente
1
No estoy seguro de por qué esto fue rechazado ... en realidad resolvió el problema que estaba teniendo.
redspidermkv
-1

Sé que esta es una publicación antigua, pero acabo de escribir esta función y la dejaré aquí en caso de que alguien tropiece buscando una respuesta:

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))
usuario3569372
fuente
Este código no hace lo que se pretende que se publique. Dar class_only o instance_only dará como resultado una lista vacía.
Oz123
-2

Esto es solo una observación. "codificar" parece ser un método para objetos de cadena

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

Sin embargo, si se inspecciona str1 en busca de métodos, se devuelve una lista vacía

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

Entonces, tal vez estoy equivocado, pero el problema parece no ser simple.

José Crespo Barrios
fuente
1
'a'es un objeto, cuyo tipo es str. Puedes ver esto corriendo type('a'). inspect.getmember()toma un parámetro de tipo, por lo que debe llamar inspect.getmember(str)para ver qué espera.
lhasson
-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('__')])

salida

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

Si desea enumerar solo los métodos de una clase python

import numpy as np
print(np.random.__all__)
Rakesh Chaudhari
fuente
1
Eso es un módulo, no una clase.
Aran-Fey
@ Aran-Fey Se aplica para cada clase, todo existe en cada clase
Rakesh Chaudhari
2
No lo hace Ni siquiera existe en todos los módulos .
Aran-Fey