У меня есть список, определенный на уровне класса в Python, и ниже приведен мой код:
class TestStudentReg(unittest.TestCase):
student_id_list = []
Я пытаюсь инициализировать список, читая значения из файла csv, и ниже приведен метод, который я использую в том же классе, т. е. TestStudentReg.
@classmethod
def initialise_student_id_list(cls):
filename = 'input/student_testdata.csv'
with open(filename, 'r') as csvfile:
datareader = (csv.reader(csvfile, delimiter = "|"))
next(datareader, None) # skip the headers
for col in datareader:
cls.student_id_list.append(col[2])
Теперь, когда этот метод является общим для нескольких классов, я планирую сделать его функцией многократного использования и определить его в отдельном файле Python (utilities.py), который будет выглядеть следующим образом:
import csv
import logging
import time
import timeit
def get_date(dateformat = "%Y-%m-%d", subtract_num_of_days=0):
getdate = date.today() - timedelta(subtract_num_of_days)
return getdate.strftime(dateformat)
def initialise_student_id_list(**kwargs):
filename = 'input/student_testdata.csv'
with open(filename, 'r') as csvfile:
datareader = (csv.reader(csvfile, delimiter = "|"))
next(datareader, None) # skip the headers
for col in datareader:
cls.student_id_list.append(col[2])
Я хотел бы понять, как инициализировать Student_id_list из файла утилит.
Один из способов реализовать это заключался в том, чтобы метод возвращал список и инициализировал значение, вызывая этот метод.
def initialise_student_id_list(**kwargs):
id_list=[]
filename = 'input/student_testdata.csv'
with open(filename, 'r') as csvfile:
datareader = (csv.reader(csvfile, delimiter = "|"))
next(datareader, None) # skip the headers
for col in datareader:
cls.student_id_list.append(col[2])
return id_list
Не уверен, что это правильный подход. Отлично, если кто-нибудь сможет мне помочь в этом, пожалуйста.
Какой смысл перемещать метод класса из модуля, в котором определен класс, в его собственный модуль? Я понимаю, что вы ожидаете, что его будут использовать несколько классов, но метод, который нужно использовать, заключается в наследовании от класса, в котором определен метод - если вы настаиваете на помещении его в отдельный модуль, поместите класс в этот модуль и создайте TestStudentReg
потомок этого класса.
@Grismar В основном я с тобой согласен. Но в некоторых случаях наследование от класса нецелесообразно. Например, если есть какой-то класс, которому нужна эта функциональность, но он не имеет никакого отношения к тому, что делает TestStudentReg
? Я думаю, что метод класса — не лучший выбор, и перенос его в отдельный модуль может быть намного лучше.
Когда функция ожидает, что класс будет передан в качестве аргумента, а затем начинает изменять содержимое этого класса (что и делает код OP), это полезно только в контексте такого класса, и, конечно, не лучше напишите это как независимую функцию. Конечно, вы могли бы полностью переписать его, чтобы он стал независимой функцией или классом, который можно было бы объединить в другой класс, но тогда мы по сути говорим: «вам нужно вообще что-то другое», и это обменивается мнениями, а не отвечает на вопрос. .
@Grismar, я согласен. Извините за предыдущий комментарий, но в любом случае я думаю, что смогу ответить на вопрос ОП.
Есть несколько способов решить вашу проблему. Здесь я перечислю три, увеличивающие читабельность и общее качество:
def student_id_list_initialiser(class_obj, **kwargs):
cls = class_obj
filename = 'input/student_testdata.csv'
with open(filename, 'r') as csvfile:
datareader = (csv.reader(csvfile, delimiter = "|"))
next(datareader, None) # skip the headers
for col in datareader:
cls.student_id_list.append(col[2])
class MyTest():
student_id_list = []
@classmethod
def initialise_student_id_list(cls):
student_id_list_initialiser(cls)
Этот подход работает нормально, но он не самый лучший, поскольку нам приходится вручную передавать объект класса, и код нарушает инкапсуляцию. Однако в сочетании с простым и коротким методом класса, который устанавливает переменную класса student_id_list
, это будет лучше:
def student_id_list_creator(**kwargs):
ret_l = []
filename = 'input/student_testdata.csv'
with open(filename, 'r') as csvfile:
datareader = (csv.reader(csvfile, delimiter = "|"))
next(datareader, None) # skip the headers
for col in datareader:
ret_l.append(col[2])
return ret_l
class MyTest():
student_id_list = []
@classmethod
def initialise_student_id_list(cls):
cls.student_id_list = student_id_list_creator()
Это похоже на то, о чем вы говорили, и да, это действительно хороший подход.
Наконец, наследование, пожалуй, лучший вариант здесь, хотя и не прямое наследование от класса TestStudentReg
. Вот код:
class StudentInfoHelper():
@classmethod
def initialise_student_id_list(cls):
filename = 'input/student_testdata.csv'
with open(filename, 'r') as csvfile:
datareader = (csv.reader(csvfile, delimiter = "|"))
next(datareader, None) # skip the headers
for col in datareader:
cls.student_id_list.append(col[2])
class TestStudentReg(StudentInfoHelper):
student_id_list = []
# outside:
TestStudentReg.initialise_student_id_list()
Этот код не только более читабелен, но и гораздо более гибок и может применяться к любому классу, если этот класс наследуется от StudentInfoHelper
. Единственным недостатком этого подхода является то, что
student_id_list
. Но поскольку вы уже выбрали имя переменной, это не будет проблемой.student_id_initialiser
. Причина здесь довольно очевидна: если вы это сделаете, исходный метод будет «затмлен» новым. Но иногда это также хорошо, если вы хотите настроить метод или не хотите его.Это здорово - спасибо. Почему в третьем подходе мы вызываем метод TestStudentReg.initialise_student_id_list() снаружи? Должно ли это быть внутри класса TestStudentReg?
@BalajiVenkatachalam Наверное. Если у вас есть метод __init__()
или другой инициализатор, поместите его туда. Поскольку я не знаю других частей вашего класса, я просто вынес вызов метода наружу. На практике предполагается, что он находится внутри какого-то метода класса TestStudentReg
.
Откуда взялась переменная
filename
в вашем методе класса? Пожалуйста, отредактируйте, чтобы показать, где это определено.