Как использовать getattr или getattribute для корректного вызова ImportError при вызове некоторых методов - PullRequest
0 голосов
/ 27 апреля 2020

Здравствуйте, заранее спасибо за помощь,

Пожалуйста, смотрите ниже код:

import types

_MSG = ("Failed importing {name}. Please install {name}."
        " Using pip install {name}")

class Empty(): # pylint: disable=too-few-public-methods
    """Empty class for beam API."""
    def __call__(self, *args, **kwargs):
        return None

class DummyBeam(types.ModuleType): # pylint: disable=too-few-public-methods

    DoFn = Empty
    Pipeline = Empty

    def __init__(self, name="apache_beam"):
        super(DummyBeam, self).__init__(name)

    def __getattribute__(self, _):
        if getattr(DummyBeam, _, None) is Empty:

            err_msg = _MSG.format(name=self.__name__)
            raise ImportError(err_msg)

Что я хочу проверить, если apache_beam не был установлен, он успешно загрузит весь луч классы, такие как DoFn и конвейер, но при вызове некоторой функции возникает ошибка, пожалуйста, смотрите код ниже, чтобы увидеть приведенный выше код.

try:
  import apache_beam as beam
except ImportError:
  beam = DummyBeam()

class SomeFn(beam.DoFn):
  pass

class SomeOtherFn(beam.Pipeline):
  pass

SomeFn()

В приведенном выше коде для доступа к beam.DoFn теперь возникает ошибка, но я не хочу, чтобы она вызывалась. ошибка при доступе к beam.DoFn, хотя возникает ошибка при вызове SomeFn(). Также попытался заменить getattribute на getattr, и это не дает мне результатов, так как я ожидал, что это не вызовет ошибку при вызове SomeFn (), хотя он работает нормально для всех кодов.

Спасибо за изучение этого.

1 Ответ

0 голосов
/ 27 апреля 2020

Как показано в трассировке (что вы должны были опубликовать FWIW), ваша ошибка не в вызове SomeFn(), а в доступе к beam.DoFn в классе SomeFn определение. И причина вполне очевидна: вы очень явно указали Python сделать это простым переопределением Beam.__getattribute__.

Обратите внимание, что object.__getattribute__ является официальной реализацией поиска атрибутов по умолчанию (она вызывается каждый раз, когда Python видит либо obj.name, либо getattr (obj, "name"), и что этот метод лучше оставьте в покое, если вы не полностью понимаете последствия его отмены И не можете найти лучшего решения.

В этом случае очень очевидное решение - вместо этого реализовать __getattr__, который вызывается только __getattribute__ в качестве последнего средства если атрибут не может быть разрешен каким-либо другим способом. Вы говорите, что:

Также заменить getattribute на getattr не работал

, но я только что попробовал его на вашем фрагмент кода и он (конечно) дает ожидаемый результат. Является ли это тем, чего ожидал , которого вы ожидали, это другой вопрос, но, поскольку вы ни опубликовали эту версию своего кода, ни пытались объяснить, как он "не работал ", вы не можете ожидать никакого ответа по этому вопросу (подсказка:" не работает "- это абсолютно бесполезное описание проблемы).

Как последнее замечание:

он успешно загрузит все методы луча, такие как DoFn и конвейер ... В приведенном выше коде сейчас вызывается луч. DoFn

Кажется, вы немного запутался в терминологии. DoFn и Pipeline являются классами, а не методами, и (как уже упоминалось) ваша ошибка возникает, когда обращается к beam.DoFn, а не когда вызывает .

РЕДАКТИРОВАТЬ:

by не работал Я имел в виду, что это не дает мне ошибку, когда я пытаюсь получить доступ к beam.DoFn или SomeFn () при использовании Вместо этого getattr getattribute (...) я хочу вызвать ошибку при вызове someFn без доступа к лучу. DoFn

Хорошо, похоже, вы не совсем получили порядок выполнения вызова метода выражение. Когда вы делаете

obj.method()

, это фактически сокращение для

method = obj.__getattribute__("method")
method.__call__()

Так что переопределение __getattribute__ не является правильным решением (см. Выше), а определение __getattr__ бесполезно здесь - ваши атрибуты DummyBeam class HAS DoFn и Pipeline, поэтому __getattr__ просто не будут вызываться для этих имен.

Теперь причина, по которой вы не получаете никаких исключений при вызове beam.DoFn или beam.Pipeline означает, что эти имена связаны с вашим Empty классом , а не с экземплярами этого класса, поэтому вы на самом деле никогда не вызываете Empty.__call__. Метод Rhe __call__, определенный в классе, используется только тогда, когда вызывается экземпляр этого класса, а не при создании экземпляра класса (в этом случае вызывается метод __call__ метакласса):

>>> class MyCallable:    
...     def __init__(self):
...         print("in MyCallable.__init__")
...     def __call__(self):
...         print("in MyCallable.__call__")
... 
>>>  
... c = MyCallable()
in MyCallable.__init__
>>> c()
in MyCallable.__call__
>>> 

Так что, если вы хотите поднять свое исключение, когда кто-то пытается создать DoFn или ̀Pipeline you either have to make them instances of Empty or keep them as they are and rename Empty. call to Пусто. new which is the first method called by type. call ( type` - метакласс по умолчанию для всех классов).

...