Изменить функцию и сохранить работоспособность кода - PullRequest
2 голосов
/ 02 апреля 2019

Я не профессиональный кодер, но время от времени я использую Python для своих научных нужд.Поэтому я хочу узнать, какой самый Pythonic способ сделать следующее:

Я работаю с уже существующим модулем, и какой-то класс там выглядит так

class ATS(Instrument):

    def __init__(self, ..., dll_path: str):
        ... 
        self._dll = ctypes.cdll.LoadLibrary(dll_path)
        ...

    def _call_dll(self, func_name: str, *args) -> None:
        func = getattr(self._dll, func_name)
        output = func(*args)
        ...

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

Вопрос в том, какой самый Pythonic способ изменить эту функцию _call_dll, чтобы явно указать, какую DLL я хочу использовать для вызова конкретной функции.В то же время я хочу сохранить работоспособность остальной части кода, где используется старая версия _call_dll.

Я вижу несколько способов сделать это, но я не уверен, какой из нихсамый профессиональный и хороший стиль.

  1. Создайте свою собственную функцию _call_dll_n для каждого dll_n, который я хочу использовать, но он не компактен и не приятен.

  2. Добавьте префикс к имени функции, чтобы указать DLL, например

    class ATS(Instrument):
        def __init__(self, ..., dll_path, dll_path_1, ...):
            ... 
            self._dll = ctypes.cdll.LoadLibrary(dll_path)
            self._dll_1 = ctypes.cdll.LoadLibrary(dll_path_1)
            ...
    
        def _call_dll(self, pre_func_name: str,  *args) -> None:
            if prefix_func_name[:5] == 'dll_1':
                dll = self._dll_1
                func_name = pre_func_name[5:]
                func = getattr(dll, func_name)
             ...
             else:
                 dll = self._dll  # Default DLL.
                 func_name = pre_func_name
    
  3. Make my_call_dll:

    def _my_call_dll(self, func_name: str, dll = None, *args))
        if dll is None:
            self._call_dll(self, func_name, *args)      
        else:
            dll_bckp = self._dll
        self._dll = dll
    
        self._call_dll(self, func_name, *args)
    
        self._dll = dll_bckp
    

Мы ценим вашу помощь в этом конкретном примере, но также приветствуются более общие идеи о том, как работать и изменять уже существующие функции / классы.

1 Ответ

0 голосов
/ 23 апреля 2019

Вам не нужно изменять код; класс ATS, как вы его представили, уже учитывает то, что вы описали. Вместо этого: создает несколько экземпляров ATS , каждый из которых указывает, какую DLL использовать.

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

  • A отображение , от некоторого ключа до пути к файлу DLL.
  • Оболочка API , позволяющая указать ключ в приведенном выше отображении при вызове API.

В Python встроенный в тип dict является естественным способом реализации отображения. Вы можете использовать простую строку в качестве ключа.

import os.path


# You might get the path to your DLL files some other way,
# for example by reading a process environment variable.
# In this example I just hard-code the path root.
dll_file_path_root = os.path.join('/usr/lib', 'ats')

dll_file_paths = {
    'foo': os.path.join(dll_file_path_root, 'foo.dll'),
    'bar': os.path.join(dll_file_path_root, 'bar_v5.dll'),
    'baz': os.path.join(dll_file_path_root, 'super_baz.dll'),
}

Существующий класс ATS, как вы его представили выше, уже реализует оболочку API. Каждый экземпляр будет содержать ссылку (свой атрибут _dll для внутреннего использования), с которой будет обращаться DLL к экземпляру ATS. Класс инициализирует каждый экземпляр ATS любой указанной вами DLL. Итак:

# Create an ATS instance that holds a reference to the ‘foo.dll’ library.
foo_ats = ATS(dll_path=dll_file_paths['foo'])
# Create an ATS instance that holds a reference to the ‘bar_v5.dll’ library.
bar_ats = ATS(dll_path=dll_file_paths['bar'])

# Call the ‘lorem’ function in the ‘foo.dll’ library.
foo_ats._call_dll(func_name='lorem')
# Call the ‘lorem’ function in the ‘bar_v5.dll’ library.
bar_ats._call_dll(func_name='lorem')

Это одно из основных преимуществ определения классов: они инкапсулируют общее поведение класса объектов, в то же время позволяя каждому объекту иметь индивидуальные атрибуты, которые их различают.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...