在Python中使用getattr调用方法
如何使用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。