2017-02-17 24 views
1

Вот очень простой Base класс, который содержит статический метод и метод класса:Как проверить, является ли метод методом класса или статическим методом в метаклассе?

class Base(): 

    @staticmethod 
    def f(): 
     print("Base.f") 

    @classmethod 
    def g(cls): 
     print("Base.g") 

    def h(self): 
     print("Base.h") 

Если класс должен быть производным от Base и переопределить либо f или g то, что staticmethod и classmethod декораторы должны для повторного использования в методах переопределения.

class A(Base): 

    @staticmethod 
    def f(): 
     print("A.f") 

class B(Base): 

    @classmethod 
    def g(cls): 
     print("B.g") 

Итак, сначала я думал, что я хотел бы создать метакласс, который автоматически делает f в staticmethod и gstaticmethod.

class BaseMeta(type): 

    def __init__(cls, name, bases, namespace): 
     super().__init__(name, bases, namespace) 
     if 'f' in namespace: cls.f = staticmethod(cls.f) 
     if 'g' in namespace: cls.g = classmethod(cls.g) 

Теперь остальные классы не должны использовать staticmethod и classmethod явно.

class Base(metaclass=BaseMeta): 

    def f(): 
     print("Base.f") 

    def g(cls): 
     print("Base.g") 

    def h(self): 
     print("Base.h") 

class A(Base): 

    def f(): 
     print("A.f") 

class B(Base): 

    def g(cls): 
     print("B.g") 

Это работает, но мне не нравится, как он выглядит. Теперь я понимаю, что staticmethod и classmethodдекораторы должны явно использоваться (в конце концов, явно лучше, чем неявное, не так ли?)

Так я думал, что я мог держать метакласса, но на этот раз вместо того, чтобы , применяя декораторов, я должен был только проверить независимо от того, были ли они использованы и выбрасывали исключение, если они этого не сделали.

class BaseMeta(type): 

    def __init__(cls, name, bases, namespace): 
     super().__init__(name, bases, namespace) 
     # check if cls.f is a static method 
     if not inspect.isfunction(cls.f): 
      raise Exception("f should be a static method") 
     # check if cls.g is a static method 
     if not (inspect.ismethod(cls.g) and cls.g.__self__ == cls): 
      raise Exception("g should be a class method") 

К сожалению, это не работает. Похоже, что в metaclasse __init__ все считается просто функцией (просто печать cls.f и cls.g делает это очевидным).

Есть ли что-то, что мне не хватает здесь?

+0

В явном ядре Python не обязательно \ обязательно \ лучше, чем неявное. Подумайте, что EAFP (проще попросить прощения, чем разрешение) считается Pythonic и мышление более высокого уровня поощряется в деталях вычислений в качестве примеров. – GRAYgoose124

ответ

1

Это:

if not (inspect.ismethod(cls.g) and cls.g.__self__ == cls): 
    raise Exception("g should be a class method") 

работает отлично, но это:

if not inspect.isfunction(cls.f): 
    raise Exception("f should be a static method") 

не делает, потому что на Python 3, cls.f будет функция f ли не был staticmethod декоратор применяется. (На Python 2, он был бы несвязанный объект метод без декоратора.)


Вместо доступа cls.f или cls.g и пытается выяснить, что это за дескриптором вы прошли через на основании результатов дескриптора протокол, обход протокола дескриптора и доступ к необработанному содержимому пространства имен определения класса:

if 'f' in namespace and not isinstance(namespace['f'], staticmethod): 
    whatever() 
if 'g' in namespace and not isinstance(namespace['g'], classmethod): 
    whatever() 
+0

Я протестировал 'inspect.isfunction', и он вернул' True' для всех методов ('f',' g' и 'h'). Я также тестировал 'inspect.ismethod', и он возвращал' False' для всех этих методов. Эти тесты фактически * работают *, если вы пытаетесь их после создания класса, но они, похоже, не работают в метаклассе. –

+0

@GeorgeBoukeas: Не могли бы вы пояснить, что должен был представлять «XX»? – user2357112

+1

@GeorgeBoukeas: [Can not reproduce.] (Http://ideone.com/288YGK) 'ismethod' возвращает' True', как и ожидалось для метода class. – user2357112

1

ОК, так что кажется, что пытается проверить cls.f или cls.g, являются ли статическими или классовыми методами в метаклассе бессмысленно, они не кажутся связаны еще.

Однако, используя декоратор staticmethod или classmethod, метод должен обязательно оставил на нем свой знак. Я играл, и в конце концов нашел то, что я изначально хотел сделать может быть реализован следующим образом:

class BaseMeta(type): 

    def __init__(cls, name, bases, namespace): 
     super().__init__(name, bases, namespace) 
     # check if cls.f is a static method 
     if 'f' in namespace and not isinstance(namespace['f'], staticmethod): 
      raise Exception(cls.__name__ + ".f should be a static method") 
     # check if cls.g is a class method 
     if 'g' in namespace and not isinstance(namespace['g'], classmethod): 
      raise Exception(cls.__name__ + ".g should be a class method") 

Таким образом, ответ на исходный вопрос:

Проверка, был ли оформлен метод с staticmethod или classmethod возможно в метаклассе, путем извлечения метода из пространства имен и проверки того, является ли он экземпляром 'staticmethod' или 'classmethod'.

+2

Вам не нужно искать '__name__'. 'isinstance (что угодно, staticmethod)' и 'isinstance (что угодно, classmethod)' будет работать нормально. – user2357112

+0

Спасибо, это, безусловно, более элегантно. Я отредактирую соответственно. –

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

  • Нет связанных вопросов^_^