Страницы

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

пятница, 15 марта 2019 г.

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

В версии 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, то не совсем представляется, куда и зачем можно впихнуть новый метод.


Ответ

Потому что вызов 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 Выбор последнего по дате файла из каталога Найти суммарный размер всех регулярных файлов в каталоге, рекурсивно обходя все подкаталоги

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

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