Нет, невозможно обернуть индикатор выполнения вокруг кода, к которому у вас нет доступа.
Кроме того, вы можете использовать интерфейс на основе iterable для tqdm только при зацикливании. над итерируемым объектом, которого здесь нет. Таким образом, вы должны использовать интерфейс на основе update
:
with tqdm(total=100) as pbar:
for i in range(10):
pbar.update(10)
Вопрос в том, как заставить PyYAML вызывать это pbar.update
?
В идеале вам нужно найти место для подключения процесса загрузки, где вы можете вызвать pbar.update
. Если это невозможно, вам придется сделать что-то уродливое (например, разветвить PyYAML
и добавить к его API или сделать то же самое во время выполнения, исправив его), или переключиться на другую библиотеку. Но это должно быть возможно.
Очевидным вариантом является создание собственного подкласса PyYAML.Loader
. Документы для PyYAML объясняют API для этого класса, поэтому вы можете переопределить любой из методов, чтобы передать некоторый прогресс, а затем super
в базовый класс.
Но, к сожалению, ни один из них не выглядит столь многообещающим. Конечно, вы можете вызвать один раз для токена, один раз для события или один раз для узла, но, не зная, сколько имеется токенов, событий или узлов, это не позволит вам показать, насколько глубоко вы находитесь в файле. Если вам нужен счетчик неопределенного прогресса, это нормально, но если вы можете получить фактический прогресс с оценкой того, сколько времени осталось пройти, и так далее, это всегда лучше.
Одна вещь, которую вы могли бы сделать, это заставить ваш Loader
подкласс вызвать tell
на его stream
, чтобы выяснить, сколько байтов вы уже прочитали.
У меня нет PyYAML на этом компьютере, а документы довольно запутанные, поэтому вам, вероятно, придется немного поэкспериментировать, но должно получиться что-то вроде этого:
class ProgressLoader(yaml.CLoader):
def __init__(self, stream, callback):
super().__init__(stream)
# __ because who knows what names the base class is using?
self.__stream = stream
self.__pos = 0
self.__callback = callback
def get_token(self):
result = super().get_token()
pos = self.__stream.tell()
self.__callback(pos - self.__pos)
self.__pos = pos
return result
Но тогда я не уверен, как заставить PyYAML передать ваш обратный вызов в конструктор ProgressLoader
, поэтому вам придется сделать что-то вроде этого:
with open(file_name, 'r') as stream:
size = os.stat(stream.fileno()).st_size
with tqdm(total=size) as progress:
factory = lambda stream: ProgressLoader(stream, progress.update)
data = yaml.load(stream, Loader=factory)
Но раз уж мы все равно перейдем к файлу, возможно, будет проще не возиться с запутанно документированными типами загрузчиков, а вместо этого просто написать оболочку файла.
Документация для файловых объектов довольно плотная< /em>, но, по крайней мере, они понятны, а на самом деле работа довольно проста:
class ProgressFileWrapper(io.TextIOBase):
def __init__(self, file, callback):
self.file = file
self.callback = callback
def read(self, size=-1):
buf = self.file.read(size)
if buf:
self.callback(len(buf))
return buf
def readline(self, size=-1):
buf = self.file.readline(size)
if buf:
self.callback(len(buf))
return buf
В настоящее время:
with open(file_name, 'r') as stream:
size = os.stat(stream.fileno()).st_size
with tqdm(total=size) as progress:
wrapper = ProgressFileWrapper(stream, progress.update)
data = yaml.load(wrapper, Loader=Loader)
Конечно, это не идеально. Здесь мы предполагаем, что вся работа заключается в чтении файла с диска, а не в его анализе. Это, вероятно, достаточно близко к истине, чтобы нам это сошло с рук, но если это не так, у вас будет один из тех индикаторов выполнения, который приближается почти к 100%, а затем просто бесполезно остается там в течение длительного времени. 1
1. Это не только ужасно раздражает, но и настолько тесно связано с Windows и другими продуктами Microsoft, что они, вероятно, могут подать на вас в суд за кражу их внешнего вида. :)
person
abarnert
schedule
06.09.2018