Мало того, что System.Collections.ObjectModel.Collection
хорошая ставка, но и в документах справки, существует пример из как переопределить его различные защищенные методы для получения уведомления. (Прокрутите вниз к Примеру 2.)
Вот как я подхожу к проблеме, используя как можно больше лени:
from random import randint
from itertools import takewhile, islice
file = "/etc/passwd"
f = open(file, "r")
f.seek(randint(10,250))
pos = f.tell()
print "pos=%d" % pos
def countbytes(iterable):
bytes = 0
for item in iterable:
bytes += len(item)
yield bytes
print 1+len(list(takewhile(lambda x: x <= pos, countbytes(open(file, "r")))))
Для менее удобочитаемого, но гораздо более ленивого подхода используйте enumerate
и ] drop while
:
from random import randint
from itertools import islice, dropwhile
file = "/etc/passwd"
f = open(file, "r")
f.seek(randint(10,250))
pos = f.tell()
print "pos=%d" % pos
def countbytes(iterable):
bytes = 0
for item in iterable:
bytes += len(item)
yield bytes
print list(
islice(
dropwhile(lambda x: x[1] <= pos, enumerate(countbytes(open(file, "r"))))
, 1))[0][0]+1
Когда вы используете seek (), python может использовать смещения указателя для перехода к желаемой позиции в файле. Но для того, чтобы узнать текущий номер строки, вам нужно изучить каждый символ до этой позиции. Так что вы можете отказаться от seek () в пользу read ():
Замените
f = open(filename, "r")
f.seek(55)
на
f = open(filename, "r")
line=f.read(55).count('\n')+1
print(line)
Возможно, вы не хотите использовать f.read (num), поскольку это может потребовать много памяти, если num очень большой. В этом случае вы можете использовать такой генератор:
import itertools
import operator
line_number=reduce(operator.add,( f.read(1)=='\n' for _ in itertools.repeat(None,num)))
pos=f.tell()
Это эквивалентно f.seek (num)
с дополнительным преимуществом предоставления вам line_number
.