Bagaimana cara mendapatkan daftar metode dalam kelas Python?

331

Saya ingin mengulangi melalui metode di kelas, atau menangani objek kelas atau contoh berbeda berdasarkan metode yang ada. Bagaimana cara saya mendapatkan daftar metode kelas?

Lihat juga:

Purrell
sumber
1
Sumber ..... sumber ...
RickyA
4
@ JonCrowell, itu bukan cara kerja the Force.
Ken Williams
Untuk Cython menggunakan compiler directive bindingworks: stackoverflow.com/a/46041480/1959808
Ioannis Filippidis
4
Pada python3: [f for f in dir(ClassName) if not f.startswith('_')]atau hanya dir(ClassName)untuk semuanya
Seraf
@ Seraf Tolong jangan memposting jawaban di komentar. Kirim jawaban sebagai gantinya.
wjandrea

Jawaban:

333

Contoh (daftar metode optparse.OptionParserkelas):

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

Perhatikan bahwa getmembersmengembalikan daftar 2-tupel. Item pertama adalah nama anggota, item kedua adalah nilainya.

Anda juga dapat mengirimkan instance ke getmembers:

>>> parser = OptionParser()
>>> inspect.getmembers(parser, predicate=inspect.ismethod)
...
kode kode
sumber
4
sempurna, bagian predikat adalah kunci, jika tidak Anda mendapatkan hal yang sama seperti dikt dengan info meta tambahan. Terima kasih.
Purrell
2
Apakah ini menghasilkan daftar semua metode di kelas (termasuk yang diwarisi dari kelas lain), atau akankah hanya daftar metode yang secara eksplisit didefinisikan di kelas itu?
Anderson Green
10
inspect.isroutinemungkin predikat yang lebih tepat; inspect.ismethodtidak bekerja untuk metode semua objek.
Gavin S. Yancey
1
Ini hanya berfungsi ketika menggunakan instance kelas (seperti contoh juga). Apakah ini perilaku yang diharapkan?
Christian Reall-Fluharty
2
pada python 3.7 setidaknya ini tidak berhasil, Anda harus membuat instance kelas
kederrac
222

Ada dir(theobject)metode untuk membuat daftar semua bidang dan metode objek Anda (sebagai tuple) dan modul inspeksi (sebagai penulisan codeape) untuk membuat daftar bidang dan metode dengan dokumen mereka (dalam "" ").

Karena semuanya (bahkan bidang) dapat dipanggil dengan Python, saya tidak yakin ada fungsi bawaan untuk mendaftar hanya metode. Anda mungkin ingin mencoba jika objek yang Anda lewati dirdapat dipanggil atau tidak.

Vincent Demeester
sumber
3
dir (objek) adalah solusi paling sederhana yang saya gunakan.
lstodd
@ skjerns Diperlukan 5 simbol untuk mengetiknya. :)
Dr_Zaszuś
117

Python 3.x menjawab tanpa pustaka eksternal

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

hasil pengecualian-dalang:

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

Katakanlah Anda ingin tahu semua metode yang terkait dengan kelas daftar. Cukup Ketikkan yang berikut

 print (dir(list))

Di atas akan memberi Anda semua metode kelas daftar

Naresh Joshi
sumber
2
Yang ini adalah solusi terbaik
Zeeshan Ahmad
3
print([ m for m in dir(my_class) if not m.startswith('__')])
Evhz
32

Coba properti itu __dict__.

Eugene Bulkin
sumber
16
Saya pikir maksud Anda dict . Tapi itu mencantumkan atribut instance, bukan metodenya.
me_and
1
... itu juga tidak berhasil untukku. Setelah berkonsultasi dengan sintaks Markdown, saya pikir maksud saya __dict__.
me_and
3
@me_and Anda mungkin melakukan "self .__ dict__" atau, lebih umum lagi, memanggil versi instance dari __dict__. Namun kelas memiliki __dict__terlalu dan yang harus menampilkan metode kelas :)
Seaux
21

Anda juga dapat mengimpor FunctionType dari jenis dan mengujinya dengan 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
sumber
Ini bekerja dengan baik untuk saya. Saya menambahkan and not x.startswith('_')pada akhir daftar pemahaman untuk saya gunakan untuk mengabaikan __init__metode pribadi.
Christopher Pearson
2
Anda dapat menyingkirkan importdari FunctionTypedengan menggunakan lambda lembaran dengan type():type(lambda:0)
Tersosauros
isinstanceakan lebih baik daripada di type(y) == FunctionTypesini.
Gloweye
13

Perhatikan bahwa Anda perlu mempertimbangkan apakah Anda ingin metode dari kelas dasar yang diwarisi (tetapi tidak diganti) termasuk dalam hasil. The dir()dan inspect.getmembers()operasi lakukan antara metode kelas dasar, tetapi penggunaan __dict__atribut tidak.

satyagraha
sumber
3

Ini juga berfungsi:

mymodule.py

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

Di file lain

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

keluaran:

['foo', 'bar']

Dari python docs:

inspect.isroutine (objek)

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

Begitu

print find_defining_class(car, 'speedometer') 

Pikirkan Python halaman 210

lewis scott diamond
sumber
3
Indentasi 5? Mengapitalisasi kata kunci? Penspasian gaya non-pep8?
Florrie
1
Tidak sekali pun konvensi pengkodean Nazi berhasil!
rbennell
1
Bagaimana ini menjawab pertanyaan?
Aran-Fey
2

Ada pendekatan ini:

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

Ketika berhadapan dengan instance kelas , mungkin akan lebih baik untuk mengembalikan daftar dengan referensi metode daripada hanya nama¹. Jika itu tujuan Anda, juga

  1. Menggunakan no import
  2. Tidak termasuk metode pribadi (mis. __init__) Dari daftar

Mungkin bermanfaat. Singkatnya, untuk kelas suka

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

Kita dapat memeriksa pengambilan contoh dengan

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

Jadi Anda bisa langsung menyebutnya:

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

¹ Kasus penggunaan:

Saya menggunakan ini untuk pengujian unit . Memiliki kelas di mana semua metode melakukan variasi dari proses yang sama - yang mengarah pada tes yang panjang, masing-masing hanya berjarak sedikit dari yang lain. KERING adalah mimpi yang jauh.

Pikir saya harus memiliki tes tunggal untuk semua metode, jadi saya membuat iterasi di atas.

Meskipun saya menyadari saya harus memperbaiki kode itu sendiri untuk menjadi KERING-tetap saja ... ini mungkin masih melayani jiwa nitpicky acak di masa depan.

Julio Cezar Silva
sumber
2

Saya hanya menyimpan ini di sana, karena jawaban teratas tidak jelas .

Ini adalah tes sederhana dengan kelas tidak biasa berdasarkan 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) )

Dan ini adalah hasil keluaran.

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

Jadi apa yang kita miliki:

  • dir berikan data tidak lengkap
  • inspect.getmembers memberikan data yang tidak lengkap dan memberikan kunci internal yang tidak dapat diakses getattr()
  • __dict__.keys()memberikan hasil yang lengkap dan andal

Mengapa suara begitu keliru? Dan di mana saya salah? Dan di mana salah orang lain yang jawabannya memiliki suara sangat rendah?

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

memberikan daftar identik sebagai

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

tidak.

Patrick B.
sumber
1

Anda bisa daftar semua metode dalam kelas python dengan menggunakan kode berikut

dir(className)

Ini akan mengembalikan daftar semua nama metode di kelas

Ekene Oguikpu
sumber
0

Jika metode Anda adalah metode "biasa" dan bukan statimethod, classmethoddll.
Ada sedikit hack yang saya buat -

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

Ini dapat diperluas ke jenis metode lain dengan mengubah "fungsi" dalam ifkondisi yang sesuai.
Diuji pada python 2.7.

markroxor
sumber
1
Tidak yakin mengapa ini dibatalkan ... itu benar-benar memecahkan masalah yang saya alami.
redspidermkv
-1

Saya tahu ini adalah posting lama, tetapi hanya menulis fungsi ini dan akan meninggalkannya di sini jika seseorang tersandung mencari jawaban:

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))
pengguna3569372
sumber
Kode ini tidak melakukan apa yang dimaksudkan sebagai dipublikasikan. Memberi class_only atau instance_only akan menghasilkan daftar kosong.
Oz123
-2

Ini hanya sebuah pengamatan. "encode" tampaknya menjadi metode untuk objek string

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

Namun, jika str1 diperiksa untuk metode, daftar kosong dikembalikan

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

Jadi, mungkin saya salah, tetapi masalah ini tampaknya tidak sederhana.

José Crespo Barrios
sumber
1
'a'adalah objek, yang tipenya str. Anda dapat melihat ini dengan menjalankan type('a'). inspect.getmember()mengambil parameter tipe, jadi Anda perlu menelepon inspect.getmember(str)untuk melihat apa yang Anda harapkan.
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('__')])

keluaran

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

Jika Anda ingin mendaftar hanya metode kelas python

import numpy as np
print(np.random.__all__)
Rakesh Chaudhari
sumber
1
Itu modul, bukan kelas.
Aran-Fey
@ Aran-Fey Berlaku untuk setiap kelas, semua ada di setiap kelas
Rakesh Chaudhari
2
Tidak, tidak. Bahkan tidak ada di semua modul .
Aran-Fey