Функция обновления файла CSV, которая может быть вызвана с несколькими типами объектов - PullRequest
1 голос
/ 03 мая 2020

Я пытаюсь создать функцию, которая обновляет данный CSV-файл (в качестве первого аргумента) и обновляет его с помощью данного объекта (в качестве второго аргумента). Он содержится в моем пакете 'utils' и будет вызываться из пользовательского интерфейса.

До сих пор я пытался написать четыре отдельные функции (это количество типов объектов, которые у меня сейчас есть), и каждая функция обновляет соответствующие файлы. иначе. Но это не очень многоразово и легко изменить в будущем, поэтому мне было интересно, есть ли лучший способ? Моя иерархия объектов выглядит следующим образом:

  1. Пользователь имеет два подкласса: Admin и Обычный
  2. Book - это отдельный класс, описывающий одну сущность книги

В настоящее время у меня есть:

import shutil
import csv
import Users as usr
import Book as bk

def update_file(path, change):
    tempfile = NamedTemporaryFile(delete=False)

    with open(path, 'rb') as old_file, tempfile:
        reader = csv.reader(old_file, delimiter=',')
        writer = csv.writer(tempfile, delimiter=',')

        if isinstance(change, bk.Book):
            # Code for extracting appropriate data to update based on the object type
        elif isinstance(change, usr.User):
            # Code for extracting appropriate data to update based on the object type
        elif isinstance(change, usr.Admin):
            # Code for extracting appropriate data to update based on the object type
        elif isinstance(change, usr.Regular):
            # Code for extracting appropriate data to update based on the object type
        else:
            print("Object type not recognised")
            # Some error handling

        for row in reader:
            # Actual update code will be here

    shutil.move(tempfile.name, path)

Эта функция будет вызываться в различных местах пользовательского интерфейса, что-то вроде этого :

update_file(utils.USERS_FILE_PATH, user_type_object)
update_file(utils.USERS_FILE_PATH, regular_user_type_object)
update_file(utils.USERS_FILE_PATH, admin_user_type_object)
update_file(utils.BOOKS_FILE_PATH, book_type_object)

После того, как я заполню пробелы с кодом извлечения, эта функция будет очень длинной и ужасной для обслуживания, но наличие четырех отдельных функций (и, возможно, больше, если я реализую новые типы объектов) выглядит как худшая идея.

Я также думал об использовании args и kwargs в этой функции, но не придумал, как это сделать без длинного списка if-elif операторов.

Есть ли лучший, более питонский c и более OOP способ сделать это?

Ответы [ 2 ]

0 голосов
/ 03 мая 2020

functools.singledispatch предоставляет способ отправки функций в зависимости от типа аргумента first , переданного в функцию.

from functools import singledispatch

@singledispatch
def extract(arg):
    # Perform default action
    raise TypeError(f"I don't know what to do with {arg}")

@extract.register(bk.Book)
def _(arg):
    # Perform book data extract
    return book_data

@extract.register(usr.User)
def _(arg):
    # Perform user data extract
    return user_data

# Define similar functions for other types

При использовании этой техники код в вопросе будет выглядеть следующим образом:

def update_file(path, change):
    tempfile = NamedTemporaryFile(delete=False)

    with open(path, 'rb') as old_file, tempfile:
        reader = csv.reader(old_file, delimiter=',')
        writer = csv.writer(tempfile, delimiter=',')

        data = extract(change)

        for row in reader:
            # do stuff with data

Если вы используете Python 3.7 или более позднюю версию, вы можете использовать аннотации типов вместо установки введите декоратор, если хотите.

0 голосов
/ 03 мая 2020

Да. Пусть все они реализуют метод, который возвращает данные для вашего CSV. Тогда вы просто пытаетесь вызвать эту функцию. В Python вам даже не нужно устанавливать наследование, чтобы сделать это. Вы просто пытаетесь вызвать функцию и перехватить исключения.

# ...
writer = csv.writer(tempfile, delimiter=',')
try:
   # If it's just a row
   writer.write(change.get_cvs_data())
   # Alternatively, you pass the writer in the method call and let the object do the writing:
   change.write_csv(writer)
except AttributeError:
   # Here you can handle if object has not implemented the method you're using.
   pass
# ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...