当给定方法不存在时调用类中的另一个方法?

sna*_*che 5 python python-3.x

假设我有一个包含多个函数的类。

class Person:
    def __init__(self): pass
    def say(self, speech): pass
    def walk(self, destination): pass
    def jump(self): pass
Run Code Online (Sandbox Code Playgroud)

当用户实例化一个 Person 时,我希望他们能够调用该类的任何方法。如果请求的方法不存在(例如Person.dance()),则应调用默认函数。

我想这可以通过理论上的魔法方法来完成 -

class Person:
    def __init__(self): pass
    def say(self, speech): pass
    def walk(self, destination): pass
    def jump(self): pass
    def sleep(self): print("Zzz")

    def __method__(self, func):
        if func.__name__ not in ['say','walk','jump']:
            return self.sleep
        else
            return func

billy = Person()
billy.dance()
>> "Zzz"
Run Code Online (Sandbox Code Playgroud)

然而,我不知道有这样的神奇方法。

有没有办法使类中不存在的方法重定向到另一个类?

重要的是,最终用户无需执行任何操作 - 从他们的角度来看,它应该可以正常工作。

che*_*ner 4

捕获未定义属性的标准方法是使用__getattr__

# But see the end of the answer for an afterthought
def __getattr__(self, attr):
    return self.sleep
Run Code Online (Sandbox Code Playgroud)

Python 不区分“常规”属性和方法;方法调用从普通的属性查找开始,其结果恰好是可调用的。那是,

billy.run()
Run Code Online (Sandbox Code Playgroud)

是相同的

f = billy.run
f()
Run Code Online (Sandbox Code Playgroud)

这意味着任何__getattr__未定义的属性都会被调用;没有办法在查找时判断结果是否会被调用。


但是,如果您只想为通用方法定义“别名”,则可以在class语句后使用循环来实现。

class Person:
    def __init__(self): pass
    def say(self, speech): pass
    def walk(self, destination): pass
    def jump(self): pass
    def do_all(self): pass

for alias in ["something", "something_else", "other"]:
    setattr(Person, alias, Person.do_all)
Run Code Online (Sandbox Code Playgroud)

您还可以在语句中进行硬编码分配class,但如果正如您提到的那样,有数百个这样的情况,那么这将很麻烦:

class Person:
    def do_all(self): pass

    something = do_all
    something_else = do_all
Run Code Online (Sandbox Code Playgroud)

(我没有尝试使用exec循环自动执行此类分配;这可能是可能的,但不建议这样做。)

您还可以在 的定义中嵌入别名列表__getattr__,想想看:

 def __getattr__(self, attr):
     if attr in ["something", "something_else", "other"]:
         return self.sleep
     else:
         raise AttributeError(f"type object 'Person' has no attribute '{attr}'")
Run Code Online (Sandbox Code Playgroud)