Страницы

Поиск по вопросам

четверг, 23 января 2020 г.

Зачем нужен __fspath__?

#python #python_3x


В версии Python3.6 запланирован (тем не менее об этом нигде прямо не говорится, кроме
строчки "Python-Version") релиз нового волшебного метода __fspath__ (https://www.python.org/dev/peps/pep-0519).
В описании сказано вроде бы ясно: 


  The goal is to facilitate the migration of users towards rich path
  objects while providing an easy way to work with code expecting str or
  bytes.


Цель - облегчить пользователям переезд на продвинутые объекты, представляющие файловые
пути, при этом предоставляя удобный способ работы с кодом, который ожидает юникодных
или байтовых строк.

Тем не менее, не совсем понятно несколько моментов:

Для кого это обновление? О каких пользователях идет речь? Каков будет типичный пример
использования, будь то в библиотеках или в прикладных программах?

Обычно я, как обыкновенный пользователь, пишу что-то вроде:

project_path = os.path.realpath(os.path.dirname(__file__))
config_path = os.path.join(project_path, 'configs', 'sub_configs', 'my_super_config.ini')
conf = open(config_path, 'r', encoding="utf-8")


и не вижу повода создавать из-за этого отдельные классы, представляющие пути. Даже
если это будут классы вроде ConfigPath, ConcreteProjectConfigPath, то не совсем представляется,
куда и зачем можно впихнуть новый метод.
    


Ответы

Ответ 1



Потому что вызов str(path) чреват ошибками. str(obj) работает для любого объекта в Питоне, но не все объекты представляют пути. Пример из PEP 519: open(str(None), 'w') может молча создать файл, вместо выбрасывания ошибки. os.fspath(path) возвращает представление path, подходящее для файловой системы и позволяет избежать silent errors: >>> import os >>> os.fspath(None) Traceback (most recent call last): File "", line 1, in TypeError: expected str, bytes or os.PathLike object, not Примеры кандидатов для __fspath__ протокола: pathlib.Path, os.DirEntry (генерируются os.scandir()). Ваш пример, можно так переписать: from pathlib import Path import appdirs # $ pip install appdirs path = Path(appdirs.user_data_dir(appname, appauthor)) config_path = path / 'configs' / 'sub_configs' / 'my_super_config.ini' conf = config_path.open(encoding="utf-8") Если вы действительно данные из файла, расположенного относительно установленного модуля, хотите, то используйте pkgutil.get_data() или setuptools' pkg_resources.resource_string(). В данном случае разницы особой нет между os.path функциями и pathlib интерфейсом. Но в целом есть преимущества в использовании специальных объектов для путей вместо общих строк—из PEP 519: At issue is the fact that while all file system paths can be represented as strings or bytes, not all strings or bytes represent a file system path. This can lead to issues where any e.g. string duck-types to a file system path whether it actually represents a path or not. Как продемонстрировано ошибкой наверху ответа иногда строки, которые не являются путями (str(None)), могут случайно как пути быть интерпретированы, а специальные объекты такие как pathlib.Path не имеют этого недостатка. Плюс, специальный объект может предоставлять более богатый специальный интерфейс, например, path.read_text(). Существуют и существовали модули, использующие объекты для представления путей, например: path.py. На практике, если есть возможность использовать Питон 3 или поставить pathlib на Питон 2, то код, который с файловой системой работает, приятней с pathlib.Path/os.DirEntry писать. Вот примеры кода, где pathlib делает код более читаемым. Вот примеры, где одна и та же функциональность реализована с и без os.scandir(): Python Выбор последнего по дате файла из каталога Найти суммарный размер всех регулярных файлов в каталоге, рекурсивно обходя все подкаталоги

Ответ 2



Это решает проблемы, которые возникают при использовании продвинутые объекты, представляющие файловые пути с модулем pathlib. Например сейчас невозможно использовать объект типа PurePath чтобы октрыть файл, необходимо конвертировать в string >>> from pathlib import PurePath >>> path = 'workspace' >>> p = PurePath(path) / 'where.txt' >>> with open(p) as f: ... print(f.readline()) ... Traceback (most recent call last): File "", line 1, in TypeError: invalid file: PurePosixPath('workspace/where.txt') >>> with open(str(p)) as f: ... print(f.readline()) ... spam foo, bar, baz __fspath__ решает это: To help elevate the representation of file system paths from their representation as strings and bytes to a richer object representation, the pathlib module [4] was provisionally introduced in Python 3.4 through PEP 428 . While considered by some as an improvement over strings and bytes for file system paths, it has suffered from a lack of adoption. Typically the key issue listed for the low adoption rate has been the lack of support in the standard library. This lack of support required users of pathlib to manually convert path objects to strings by calling str(path) which many found error-prone. One issue in converting path objects to strings comes from the fact that the only generic way to get a string representation of the path was to pass the object to str() . This can pose a problem when done blindly as nearly all Python objects have some string representation whether they are a path or not, e.g. str(None) will give a result that builtins.open() [5] will happily use to create a new file.

Комментариев нет:

Отправить комментарий