Почему python допускает пустую функцию (с doc-string) без инструкции "pass"?

class SomeThing(object):
    """Represents something"""

    def method_one(self):
        """This is the first method, will do something useful one day"""

    def method_two(self, a, b):
        """Returns the sum of a and b"""
        return a + b

В недавнем обзоре некоторого кода, аналогичного приведенному выше, коллега спросил:

Почему method_one успешно разбирается и принимается python? Не нужна ли пустой функции тело, состоящее всего из pass? то есть, не должно ли оно выглядеть так?

def method_one(self):
    """This is the first method, will do something useful one day"""
    pass

Мой ответ в то время был примерно таким:

Хотя docstring обычно не считается частью тела функции, поскольку он не выполняется, он анализируется как таковой, поэтому pass можно опустить.

В духе обмена знаниями Q & A style, я думал, что разместил бы более строгий ответ здесь.

Ответы

Ответ 1

В соответствии с спецификацией грамматики Python 2.7.5, которая считывается генератором синтаксического анализатора и используется для анализа исходных файлов Python, функция выглядит например:

funcdef: 'def' NAME parameters ':' suite

Тело функции - это suite, который выглядит следующим образом:

suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT

Следуя этому пути через грамматику, stmt может быть expr_stmt, который может быть просто testlist, который может быть всего лишь одним test, который может (в конечном счете) быть просто atom, который может быть только одним STRING. Докстоун.

Вот только соответствующие части грамматики, в правильном порядке:

stmt: simple_stmt | compound_stmt
simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
small_stmt: (expr_stmt | print_stmt  | del_stmt | pass_stmt | flow_stmt |
             import_stmt | global_stmt | exec_stmt | assert_stmt)
expr_stmt: testlist (augassign (yield_expr|testlist) |
                     ('=' (yield_expr|testlist))*)
testlist: test (',' test)* [',']
test: or_test ['if' or_test 'else' test] | lambdef
or_test: and_test ('or' and_test)*
and_test: not_test ('and' not_test)*
not_test: 'not' not_test | comparison
comparison: expr (comp_op expr)*
comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
expr: xor_expr ('|' xor_expr)*
xor_expr: and_expr ('^' and_expr)*
and_expr: shift_expr ('&' shift_expr)*
shift_expr: arith_expr (('<<'|'>>') arith_expr)*
arith_expr: term (('+'|'-') term)*
term: factor (('*'|'/'|'%'|'//') factor)*
factor: ('+'|'-'|'~') factor | power
power: atom trailer* ['**' factor]
atom: ('(' [yield_expr|testlist_comp] ')' |
       '[' [listmaker] ']' |
       '{' [dictorsetmaker] '}' |
       '`' testlist1 '`' |
       NAME | NUMBER | STRING+)