2010-05-26 3 views
0

Как вызвать метод с помощью getattr? Я хочу создать метакласс, который может вызывать несуществующие методы другого класса, которые начинаются со слова «oposite_». Метод должен иметь одинаковое количество аргументов, но возвращать противоположный результат.Вызов метода с getattr в Python

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 

Как с этим бороться?

+8

Для записи «oposite» пишется с двумя p. Но, как говорил мой коллега, «программистам не нужно правильно писать, они должны постоянно повторяться». – Seth

ответ

0

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

+0

Это только один оператор возврата. Но даже с этим проблема остается. –

0
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

ОК, но это так, если у yep нет аргументов, кроме самого себя. Если yep принимает другой аргумент, он не работает. –

0

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

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

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

p.oposite_yep 

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

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

Так что, когда вы это делаете:

p.oposite_yep() 

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

Смежные вопросы