Избегая логических параметров при инициализации класса для объекта контрольной суммы


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

Изначально я создал что-то довольно простое, который выбран, является ли удаленный узел с помощью POSIX или пути Windows как логический переключатель.

from pathlib import Path, PurePosixPath, PureWindowsPath

class CheckFileNonCohesive(CheckFile):
    __slots__ = ['checksum', 'filepath', 'path']
    def __init__(self, checksum: str, filepath: str, *, windows: bool=False, posix: bool=False):
        if windows and posix:
            raise ValueError("windows and posix can't both be True")
        if windows:
            self.path = PureWindowsPath
        elif posix:
            self.path = PurePosixPath
        else:
            path = Path
        self.checksum = str(checksum)
        self.filepath = self.path(str(filepath))

Однако, я не уверен, насколько сплоченной этот класс был. Было странно пройти в Windows и POSIX в качестве параметра инициализации. Потом я разделил его на три класса. Я считаю, что это если следовать общим рекомендациям, но я не уверен, что изменения стоит ли это перебор для данного варианта использования. Вот заключительные занятия для обзора:

from pathlib import Path, PurePosixPath, PureWindowsPath
import typing

PathTypes = typing.Union[Path, PureWindowsPath, PurePosixPath]


def driveless_str(path: PathTypes):
    if path.drive:
        return str(path).replace(path.drive, "", 1)
    return str(path)


class CheckFile:
    """A representation of a point in time of a file with a full path and checksum

    For information on path naming conventions see PATH_NAMING.md
    """
    __slots__ = 'checksum', 'filepath'
    path = Path

    def __init__(self, checksum: str, filepath: str):
        """
        :param checksum: Checksum of the file, int's can be passed in, but will be converted to strings
        :param filepath: Full absolute filepath
           note: as CheckFiles are often on remote files the absolute path without redirects is safest as pathlib is
                 unlikely to be able to follow
        """
        self.checksum = str(checksum)
        self.filepath = self.path(str(filepath))

    def __eq__(self, other):
        return self.filepath == other.filepath and self.checksum == other.checksum

    def __hash__(self):
        return hash((self.checksum, self.filepath, self.path))

    @property
    def basename(self) -> str:
        return self.path(self.filepath).name

    def equal_by_basename(self, other: 'CheckFile') -> bool:
        return self.checksum == other.checksum and self.basename == other.basename

    def equal_with_dirname(self, other: 'CheckFile', dirname: str) -> bool:
        return self.checksum == other.checksum and self.path(dirname + self.basename) == other.path(other.filepath)

    def not_equal_with_dirname(self, other: 'CheckFile', dirname: str) -> bool:
        return self.checksum != other.checksum and self.path(dirname + self.basename) == other.path(other.filepath)

    def equal_with_mountpoint(self, other: 'CheckFile', mountpoint: str) -> bool:
        mounted_filepath = self.prepend_path(mountpoint, self.filepath)
        return self.checksum == other.checksum and mounted_filepath == other.filepath

    def _prepend_path_to_str(self, prepend: str, filepath: PathTypes) -> str:
        prepend_path = self.path(prepend)
        if prepend_path.drive or filepath.drive:
            return prepend_path.drive + driveless_str(prepend_path) + driveless_str(filepath)
        return prepend + str(filepath)

    def prepend_path(self, prepend: str, filepath: PathTypes) -> PathTypes:
        return self.path(self._prepend_path_to_str(prepend, filepath))


class PosixCheckFile(CheckFile):
    __slots__ = ()
    path = PurePosixPath

    def _prepend_path_to_str(self, prepend: str, filepath: PathTypes) -> str:
        return prepend + str(filepath)


class WindowsCheckFile(CheckFile):
    __slots__ = ()
    path = PureWindowsPath

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

class CFFactory:
    def __new__(cls, *args, **kwargs):
        windows = kwargs.pop('windows', None)
        posix = kwargs.pop('posix', None)

        if windows and posix:
            raise ValueError("windows and posix can't both be True")
        if windows:
            return type.__call__(WindowsCheckFile, *args, **kwargs)
        if posix:
            return type.__call__(PosixCheckFile, *args, **kwargs)
        return type.__call__(CheckFile, *args, **kwargs)

Однако, если я нахожу, что я использую много этот завод был не лучше ли просто использовать что-то более похожим на начальном этапе внедрения?



91
1
задан 16 марта 2018 в 11:03 Источник Поделиться
Комментарии
1 ответ

Второй подход чувствует себя чище, как пользователь может выбрать для использования

file_checker = module.WindowsCheckFile(...)

напрямую. И завод по-прежнему можно использовать, если необходимость принятия решения во время выполнения.

Однако, есть несколько вещей, которые беспокоят меня об этой фабрике. Для стартера, нет необходимости использовать класс: простая функция будет делать. Во-вторых, вместо popИнг значения по умолчанию, вы можете вставить их в подпись. Наконец type.__call__ действительно здесь, вы можете позвонить классе напрямую:

def CheckFileFactory(*args, windows=None, posix=None, **kwargs):
if windows and posix:
raise ValueError("windows and posix can't both be True")
if windows:
return WindowsCheckFile(*args, **kwargs)
if posix:
return PosixCheckFile(*args, **kwargs)
return CheckFile(*args, **kwargs)

1
ответ дан 17 марта 2018 в 10:03 Источник Поделиться