Ответ 1
Все зависит от ситуации. Например, если вы используете инъекцию зависимостей для целей тестирования - так что вы можете легко издеваться над чем-то - вы можете часто отказаться от инъекций: вы можете вместо этого выкрикнуть модуль или класс, которые вы в противном случае вводили бы:
subprocess.Popen = some_mock_Popen
result = subprocess.call(...)
assert some_mock_popen.result == result
subprocess.call()
будет вызывать subprocess.Popen()
, и мы можем издеваться над этим, не добавляя зависимостей особым образом. Мы можем просто заменить subprocess.Popen
непосредственно. (Это просто пример, в реальной жизни вы сделаете это гораздо более надежным способом.)
Если вы используете инъекцию зависимостей для более сложных ситуаций или когда насмехаются целые модули или классы не подходит (потому что, например, вы хотите только макетировать один конкретный вызов), то с помощью атрибутов класса или глобальных глобалов модулей для зависимостей это обычный выбор. Например, учитывая my_subprocess.py
:
from subprocess import Popen
def my_call(...):
return Popen(...).communicate()
Вы можете легко заменить только вызов Popen
, сделанный my_call()
, назначив my_subprocess.Popen
; это не повлияет на другие вызовы subprocess.Popen
(но, конечно, заменит все вызовы на my_subprocess.Popen
). Аналогично, атрибуты класса:
class MyClass(object):
Popen = staticmethod(subprocess.Popen)
def call(self):
return self.Popen(...).communicate(...)
При использовании атрибутов класса, подобных этому, что редко бывает необходимо с учетом параметров, вы должны позаботиться о том, чтобы использовать staticmethod
. Если вы этого не сделаете, и объект, который вы вставляете, является обычным функциональным объектом или другим типом дескриптора, таким как свойство, которое делает что-то особенное при извлечении из класса или экземпляра, было бы неправильно. Хуже того, если вы использовали то, что прямо сейчас не является дескриптором (например, классом subprocess.Popen
, в примере), оно будет работать сейчас, но если объект, о котором идет речь, будет изменен на нормальную функцию в будущем, он будет растерянно разорваться.
Наконец, есть только обратные вызовы; если вы просто хотите привязать конкретный экземпляр класса к определенной службе, вы можете просто передать службу (или один или несколько методов службы) в инициализатор класса и использовать это:
class MyClass(object):
def __init__(self, authenticate=None, authorize=None):
if authenticate is None:
authenticate = default_authenticate
if authorize is None:
authorize = default_authorize
self.authenticate = authenticate
self.authorize = authorize
def request(self, user, password, action):
self.authenticate(user, password)
self.authorize(user, action)
self._do_request(action)
...
helper = AuthService(...)
# Pass bound methods to helper.authenticate and helper.authorize to MyClass.
inst = MyClass(authenticate=helper.authenticate, authorize=helper.authorize)
inst.request(...)
При настройке таких атрибутов экземпляров вам никогда не придется беспокоиться о стрельбе дескрипторов, поэтому просто назначать функции (или классы или другие вызовы или экземпляры) в порядке.