修改功能并保持代码可操作性

时间:2019-04-02 03:42:46

标签: python coding-style

我不是专业的编码人员,但是我会不时地使用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中的函数名称可以相同)。

问题是:修改_call_dll函数以显式指定我要使用哪个DLL来调用特定函数的最Python方式是什么。同时,我希望保留使用旧版本_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. 创建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 :(得分:0)

您无需修改​​代码;正如您介绍的那样,ATS类已经可以满足您的描述。而是:创建多个ATS实例,每个实例指定要使用的DLL。

您对问题的描述包括两个部分:

  • mapping,从某个密钥到DLL文件路径。
  • 一个API wrapper,可让您在调用API时在上述映射中指定一个键。

在Python中,built-in ‘dict’ type是实现映射的自然方法。您可以使用纯字符串作为密钥。

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属性),ATS实例将与其对话的DLL。该类将使用您指定的任何DLL初始化ATS的每个实例。所以:

# 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')

这是定义类的主要好处之一:它们封装了一类对象的常见行为,同时允许每个对象具有区分它们的单独属性。