如何在Python中检查方法是否存在?

125
在函数__getattr__()中,如果没有找到所引用的变量,就会产生一个错误。我该如何检查一个对象中是否存在某个变量或方法?
import string
import logging

class Dynamo:
 def __init__(self,x):
  print "In Init def"
  self.x=x
 def __repr__(self):
  print self.x
 def __str__(self):
  print self.x
 def __int__(self):
  print "In Init def"
 def __getattr__(self, key):
    print "In getattr"
    if key == 'color':
        return 'PapayaWhip'
    else:
        raise AttributeError


dyn = Dynamo('1')
print dyn.color
dyn.color = 'LemonChiffon'
print dyn.color
dyn.__int__()
dyn.mymethod() //How to check whether this exist or not
10个回答

148
检查类是否有这样的方法?
hasattr(Dynamo, key) and callable(getattr(Dynamo, key))

你可以使用self.__class__代替Dynamo

27
None 不可调用,因此您可以使用 callable(getattr(Dynamo, 'mymethod', None))。我选择了这个答案,因为我的 super().mymethod() 可能会引发 AttributeError - sbutler
很有趣,这个可以工作。根据PyCharm的签名,getattr的签名是def getattr(object, name, default=None):。我怀疑这不准确,因为如果是这样,我希望将None作为第三个参数传递不会改变函数的行为。 - maltem-za
2
@bszom:在Python shell中,help(getattr)说:“当给出默认参数时,如果属性不存在,则返回该参数;否则,在这种情况下会引发异常。”--(实际上,您可以检查getattr是否在缺少属性时引发异常),因此很明显,无论PyCharm是什么,它都是错误的。 - ShreevatsaR
@ShreevatsaR 感谢您确认我的怀疑。PyCharm是一个集成开发环境。 - maltem-za
@bszom 我发现这里有一些关于getattr行为的讨论:https://dev59.com/3o7ea4cB1Zd3GeqPIPif - Mahdi
10
如果你想检查当前类的实例是否有一个属性并且该属性是可调用的,可以这样做:if hasattr(self, "work") and callable(self.work)。这会检查实例是否具有"work"属性(可能是变量或函数),然后检查它是否是可调用的(表示它是一个函数)。 - Mitch McMabers

118

比起征得许可,请求宽恕要容易得多。

不要检查方法是否存在。不要在“检查”上浪费一行代码。

try:
    dyn.mymethod() # How to check whether this exists or not
    # Method exists and was used.  
except AttributeError:
    # Method does not exist; What now?

71
也许他真的不想打电话,只是想检查是否有那种方法(就像我的情况一样)... - Flavius
64
请注意,如果dyn.mymethod()本身引发AttributeError,则此操作将失败。 - D K
11
如@DK所说,这会捕获可能由正在检查的方法引发的任何AttributeError,这可能是不希望发生的(更不用说在这种情况下错误地推断出该方法不存在了)。 - ShreevatsaR
6
原文:Good in principle, and Python does have an "exceptions as control flow" culture unlike other languages. However, if you are using exception logging tools such as Sentry/Raven, or New Relic, such exceptions have to be filtered out individually (if that's possible) or generate noise. I would prefer to check if the method exists rather than calling it.翻译:原则上是好的,Python确实有与其他语言不同的“异常作为控制流”的文化。但是,如果您正在使用Sentry/Raven、New Relic等异常日志记录工具,则必须单独过滤这些异常(如果可能的话),否则会产生噪音。我更喜欢检查方法是否存在而不是调用它。 - RichVel
5
这个错误有很多方面。这种方法本身可能会引发AttributeError,这将被检测为该方法不存在!它还破坏了调试器对异常断点的支持。我相信这可能会影响性能,如果在循环中使用的话。最后但并非最不重要的是,我可能不想执行方法,只是验证它是否存在。你应该考虑删除这个答案或者至少提供所有这些警告,这样幼稚的人就不会受到误导。 - Shital Shah
显示剩余9条评论

107

在使用getattr()函数之前,怎样尝试使用dir()函数呢?

>>> "mymethod" in dir(dyn)
True

12
这并不检查它是一个方法还是一个变量。 - hek2mgl
1
使用 dir 不是很好——它不能确认名称是否为实例方法。 - Tony Suffolk 66

30

我使用以下实用函数。它适用于 lambda 表达式、类方法和实例方法。

实用方法

def has_method(o, name):
    return callable(getattr(o, name, None))

使用示例

让我们定义一个测试类

class MyTest:
  b = 'hello'
  f = lambda x: x

  @classmethod
  def fs():
    pass
  def fi(self):
    pass

现在可以尝试一下了,

>>> a = MyTest()                                                    
>>> has_method(a, 'b')                                         
False                                                          
>>> has_method(a, 'f')                                         
True                                                           
>>> has_method(a, 'fs')                                        
True                                                           
>>> has_method(a, 'fi')                                        
True                                                           
>>> has_method(a, 'not_exist')                                       
False                                                          

3
我认为这个答案比其他答案更适合,因为它没有使用一般方法(使用try语句),并且它检查成员是否实际是函数。非常好的分享! - ymz

19

您可以尝试使用“inspect”模块:

import inspect
def is_method(obj, name):
    return hasattr(obj, name) and inspect.ismethod(getattr(obj, name))

is_method(dyn, 'mymethod')

+1 对于指向inspect模块的指示。然而,ismethod在父类的方法上会失败。你的对象可能具有来自object的默认*__repr__*,但它不是你的类的方法。 - Yaroslav Nikitenko
指向 inspect 模块得到 +1,但 ismethod 会失败于来自父类的方法。你的对象可能有来自 object 的默认 *__repr__*,但它不是你的类的一个方法。 - Yaroslav Nikitenko

4
也许像这样,假设所有的方法都是可调用的。
app = App(root) # some object call app 
att = dir(app) #get attr of the object att  #['doc', 'init', 'module', 'button', 'hi_there', 'say_hi']

for i in att: 
    if callable(getattr(app, i)): 
        print 'callable:', i 
    else: 
        print 'not callable:', i

4
不妨在`dyn.__dict__`中查找一下,怎么样?
try:
    method = dyn.__dict__['mymethod']
except KeyError:
    print "mymethod not in dyn"

按照惯例,以下划线为前缀的方法意味着“私有”。 - xtofl
双下划线前缀加双下划线后缀表示“这通常由Python解释器本身使用”,通常有一些方法可以从用户程序中实现相同的效果,以满足最常见的用例(在这种情况下,使用点符号表示属性),但如果您的用例确实需要它,则既不被禁止也不是错误的。 - deStrangis
这并不是被禁止的。错误性是主观的:它取决于你想要让遵循惯例的程序员感到困惑的程度:除非你没有其他选择,否则不要使用它。 - xtofl

3
如果你的方法不在类中,而且你不想运行它并在它不存在时引发异常:
可以使用以下代码:'mymethod' in globals()

1
def myadd(x,y): return x+y
'myadd' in globals()
True
- gerowam
如果它不在一个类中,那就是一个函数,而不是一个方法。 - undefined

0
喜欢简洁的人们。

class ClassName:
    def function_name(self):
        return

class_name = ClassName()
print(dir(class_name))
# ['__init__', .... ,'function_name']

answer = 'function_name' in dir(class_name)
print("is'function_name' in class ? >> {answer}")
# is 'function_name' in class ? >> True

如果有任何负面评价,请告诉我, 这样我就可以删除或编辑帖子! - niek tuytel

-1

我认为你应该看一下 inspect 包。它允许您“包装”某些内容。当您使用 dir 方法时,它还会列出内置方法、继承方法和所有其他属性,这可能导致冲突,例如:

class One(object):

    def f_one(self):
        return 'class one'

class Two(One):

    def f_two(self):
        return 'class two'

if __name__ == '__main__':
    print dir(Two)

你从 dir(Two) 得到的数组包含了 f_onef_two 还有大量内置的东西。通过使用 inspect,你可以这样做:

class One(object):

    def f_one(self):
        return 'class one'

class Two(One):

    def f_two(self):
        return 'class two'

if __name__ == '__main__':
    import inspect

    def testForFunc(func_name):
        ## Only list attributes that are methods
        for name, _ in inspect.getmembers(Two, inspect.ismethod):
            if name == func_name:
                return True
        return False

    print testForFunc('f_two')

这个例子仍然在两个类中列出了两种方法,但如果您想将检查限制为特定类中的函数,则需要更多的工作,但绝对是可能的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接