【问题标题】:how to use pkgutils.get_data with csv.reader in python?如何在 python 中将 pkgutils.get_data 与 csv.reader 一起使用?
【发布时间】:2011-06-27 14:05:58
【问题描述】:

我有一个 python 模块,其中包含需要在运行时加载的各种数据文件(一组代表曲线的 csv 文件)。 csv 模块运行良好

  # curvefile = "ntc.10k.csv"
  raw = csv.reader(open(curvefile, 'rb'), delimiter=',')

但是如果我将此模块导入另一个脚本,我需要找到数据文件的完整路径。

/project
   /shared
       curve.py
       ntc.10k.csv
       ntc.2k5.csv
   /apps
       script.py

我希望 script.py 仅通过基本文件名引用曲线,而不是完整路径。在模块代码中,我可以使用:

pkgutil.get_data("curve", "ntc.10k.csv") 

在查找文件时效果很好,但它返回已经读入的 csv 文件,而 csv.reader 需要文件句柄本身。有什么办法可以让这两个模块一起玩得很好?它们都是标准库模块,所以我并没有真正期待问题。我知道我可以开始拆分 pkgutil 二进制文件数据,但我可能不使用 csv 库。

我知道我可以在模块代码中使用它,而忘记 pkgutils,但看起来 pkgutils 确实正是它的用途。

this_dir, this_filename = os.path.split(__file__)
DATA_PATH = os.path.join(this_dir, curvefile)
raw = csv.reader(open(DATA_PATH, "rb"))

【问题讨论】:

    标签: python


    【解决方案1】:

    我将源代码打开到get_data,让它返回文件的路径而不是加载的文件是微不足道的。这个模块应该可以解决问题。使用关键字as_string=True返回读入内存的文件,或使用as_string=False返回路径。

    import os, sys
    
    from pkgutil import get_loader
    
    def get_data_smart(package, resource, as_string=True):
    """Rewrite of pkgutil.get_data() that actually lets the user determine if data should
    be returned read into memory (aka as_string=True) or just return the file path.
    """
    
    loader = get_loader(package)
    if loader is None or not hasattr(loader, 'get_data'):
        return None
    mod = sys.modules.get(package) or loader.load_module(package)
    if mod is None or not hasattr(mod, '__file__'):
        return None
    
    # Modify the resource name to be compatible with the loader.get_data
    # signature - an os.path format "filename" starting with the dirname of
    # the package's __file__
    parts = resource.split('/')
    parts.insert(0, os.path.dirname(mod.__file__))
    resource_name = os.path.join(*parts)
    if as_string:
        return loader.get_data(resource_name)
    else:
        return resource_name
    

    【讨论】:

      【解决方案2】:

      这并不理想,尤其是对于非常大的文件,但您可以使用StringIO 将字符串转换为带有 read() 方法的内容,csv.reader 应该能够处理。

      csvdata = pkgutil.get_data("curve", "ntc.10k.csv") 
      csvio = StringIO(csvdata)
      raw = csv.reader(csvio)
      

      【讨论】:

      • 谢谢,它的工作原理和标签一样。 :) (除了现在鼻子测试对我的包层次结构有问题,但那是我自己的包装问题)
      【解决方案3】:

      在提出这个问题 10 多年后,但我使用 Google 来到这里并进入了其他答案中发布的兔子洞。如今,这似乎更直接。下面是我使用 stdlib 的 importlib 的实现,它将文件系统路径作为字符串返回到包的资源。应该适用于 3.6+。

      import importlib.resources
      import os
      
      
      def get_data_file_path(package: str, resource: str) -> str:
          """
          Returns the filesystem path of a resource marked as package
          data of a Python package installed.
      
          :param package: string of the Python package the resource is
                          located in, e.g. "mypackage.module"
          :param resource: string of the filename of the resource (do not
                           include directory names), e.g. "myfile.png"
          :return: string of the full (absolute) filesystem path to the
                   resource if it exists.
          :raises ModuleNotFoundError: In case the package `package` is not found.
          :raises FileNotFoundError: In case the file in `resource` is not
                                     found in the package.
          """
          # Guard against non-existing files, or else importlib.resources.path
          # may raise a confusing TypeError.
          if not importlib.resources.is_resource(package, resource):
              raise FileNotFoundError(f"Python package '{package}' resource '{resource}' not found.")
      
          with importlib.resources.path(package, resource) as resource_path:
              return os.fspath(resource_path)
      

      【讨论】:

        【解决方案4】:

        另一种方法是使用 json.loads() 和 file.decode()。由于 get_data() 以字节形式检索数据,因此需要将其转换为字符串才能将其处理为 json

        import json
        import pkgutil
        data_file = pkgutil.get_data('test.testmodel', 'data/test_data.json')
        length_data_file = len(json.loads(data_file.decode()))
        

        Reference

        【讨论】:

          猜你喜欢
          • 2020-05-17
          • 1970-01-01
          • 2017-09-05
          • 2020-01-29
          • 2011-09-07
          • 2016-05-31
          • 2019-11-13
          • 2013-11-06
          • 1970-01-01
          相关资源
          最近更新 更多