在Python中使用getattr调用方法

1 投票
3 回答
4312 浏览
提问于 2025-04-15 23:13

如何使用getattr调用一个方法?我想创建一个元类,这个元类可以调用其他类中那些以'oposite_'开头的不存在的方法。这个方法应该有相同数量的参数,但返回的结果要相反。

def oposite(func):
    return lambda s, *args, **kw:  not oposite(s, *args, **kw)


class Negate(type):
    def __getattr__(self, name):
        if name.startswith('oposite_'):
            return oposite(self.__getattr__(name[8:]))        
    def __init__(self,*args,**kwargs):
        self.__getattr__ = Negate.__getattr__


class P(metaclass=Negate):
    def yep(self):
        return True
    def maybe(self, sth):
        return sth

但问题是

self.__getattr__(sth) 

返回的是一个NoneType对象。

>>> p = P()
>>> p.oposite_yep()    #should be False
Traceback (most recent call last):
  File "<pyshell#115>", line 1, in <module>
    p.oposite_yep()
TypeError: <lambda>() takes at least 1 positional argument (0 given)
>>> p.oposite_maybe(False)    #should be true

该如何处理这个问题呢?

3 个回答

0

错误信息是 "TypeError: <lambda>() takes at least 1 positional argument (0 given)"

这个问题和你使用的元类魔法没有关系,主要是因为你没有给 lambda 函数传递任何参数。

当你这样做的时候:

p.oposite_yep

它会调用 __getattr__,这个方法会返回 opposite() 的结果,而这个结果是一个 lambda 函数。

你的 lambda 函数是动态创建的,并没有和实例绑定在一起。因此,它不会接收到 'self' 作为第一个参数:它只是一个临时的匿名函数。

所以当你这样做的时候:

p.oposite_yep()

你实际上是在调用这个 lambda 函数,但没有传递任何参数,这就导致了错误。

0

你忘了处理一种情况,就是属性名称并不是'oposite_'开头的情况。

2
class Negate(type):
    def __getattr__(self, name):
        if name.startswith('oposite_'):
            def oposite(*args,**kwargs):
                return not getattr(self,name[8:])(*args,**kwargs)
            return oposite
        else:
            raise AttributeError("%r object has no attribute %r" %
                                 (type(self).__name__, name))

    def __init__(self,*args,**kwargs):
        self.__getattr__ = Negate.__getattr__

class P(metaclass=Negate):
    def yep(self):
        return True
    def same(self,x,y):
        return x==y

p=P()
print(p.oposite_yep())
# False
print(p.oposite_same(1,2))
# True
print(p.nope())
def Negater(cls):
    def __getattr__(self, name):
        if name.startswith('oposite_'):
            def oposite(*args,**kwargs):
                return not getattr(self,name[8:])(*args,**kwargs)
            return oposite
        else:
            raise AttributeError("%r object has no attribute %r" %
                                 (type(self).__name__, name))
    setattr(cls,'__getattr__',__getattr__)
    return cls

@Negater
class P():
    ....

顺便说一下,你也可以用类装饰器来实现这个功能,而不是使用 metaclass。

撰写回答