Вызов метода с getattr в Python - PullRequest
0 голосов
/ 27 мая 2010

Как вызвать метод с помощью 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 голосов
/ 27 мая 2010
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():
    ....
0 голосов
/ 22 декабря 2011

Ошибка "TypeError: <lambda>() takes at least 1 positional argument (0 given) "

Это не имеет никакого отношения к вашей магии метакласса, это потому, что вы не передаете аргумент функции lambda.

Когда вы делаете это:

p.oposite_yep

Это вызов __getattr__, который возвращает результат opposite(), который является lambda функцией.

Ваша функция lamda возвращается, она создается на лету и никогда не привязывается к экземпляру. В качестве первого аргумента он не получит 'self': это просто анонимная функция, возвращаемая на лету.

Итак, когда вы сделаете это:

p.oposite_yep()

Вы в основном вызываете лямбду, без каких-либо аргументов, которые вызывают ошибку.

0 голосов
/ 27 мая 2010

Вы забыли обработать случай, когда имя атрибута не начинается с 'oposite_'.

...