Korzystając z poniższych, jestem w stanie pomyślnie utworzyć parser i dodać moje argumenty do self._parser za pomocą metody __init()__.

class Parser:
    _parser_params = {
        'description': 'Generate a version number from the version configuration file.',
        'allow_abbrev': False
    }
    _parser = argparse.ArgumentParser(**_parser_params)

Teraz chcę podzielić argumenty na grupy, więc zaktualizowałem swój moduł, dodając kilka klas reprezentujących grupy argumentów (w rzeczywistości istnieje kilka podklas klasy ArgumentGroup) i aktualizując klasę Parser .

class ArgumentGroup:
    _title = None
    _description = None

    def __init__(self, parser) -> ArgumentParser:
        parser.add_argument_group(*self._get_args())

    def _get_args(self) -> list:
        return [self._title, self._description]


class ArgumentGroup_BranchType(ArgumentGroup):
    _title = 'branch type arguments'


class Parser:
    _parser_params = {
        'description': 'Generate a version number from the version configuration file.',
        'allow_abbrev': False
    }
    _parser = argparse.ArgumentParser(**_parser_params)
    _argument_groups = [cls(_parser) for cls in ArgumentGroup.__subclasses__()]

Jednak teraz widzę błąd.

Traceback (most recent call last):
  ...
  File "version2/args.py", line 62, in <listcomp>
    _argument_groups = [cls(_parser) for cls in ArgumentGroup.__subclasses__()]
NameError: name '_parser' is not defined

Czego nie rozumiem, to dlaczego _parser_params istnieją, gdy są odwoływane przez inny atrybut klasy, ale _parser pozornie nie istnieje w tym samym scenariuszu? Jak mogę zrefaktoryzować mój kod, aby dodać wymagane grupy parserów?

0
David Gard 24 styczeń 2022, 17:24

1 odpowiedź

Najlepsza odpowiedź

Wynika to z połączenia dwóch dziwactw Pythona:

  1. Instrukcje class nie tworzą nowego zakresu lokalnego
  2. Listy składane do tworzą nowy zakres lokalny.

W rezultacie nazwa _parser znajduje się w lokalnym zasięgu, którego najbliższym zasięgiem jest zasięg globalny, więc nie może odnosić się do atrybutu klasy, która ma być.

Prostym obejściem byłoby zastąpienie rozumienia listy zwykłą pętlą for.

_argument_groups = []
for cls in ArgumentGroup.__subclasses()__:
    _argument_groups.append(cls(_parser))

(Lepszym rozwiązaniem byłoby prawdopodobnie zaprzestanie używania atrybutów klas, gdy atrybuty instancji mają większy sens).

1
chepner 24 styczeń 2022, 17:38
Dziękuję za odpowiedź i mogę potwierdzić, że obejście się sprawdziło. Nie jestem jednak ekspertem od Pythona, więc czy mógłbyś wyjaśnić, dlaczego atrybuty instancji miałyby więcej sensu niż atrybuty klas? Czy to po prostu dlatego, że w ten sposób działa to w tym przypadku, czy jest jakaś nadrzędna zasada, której mi brakuje?
 – 
David Gard
24 styczeń 2022, 18:28