Мне нужно переопределить метод родительского класса, который является генератором, и я задаюсь вопросом, как правильно это сделать. Есть ли что-то неправильное в следующем или более эффективном?
Ответ 4
Ваш код правильный.
Вернее, я не вижу в этом проблемы, и она, по-видимому, работает правильно.
Единственное, о чем я могу думать, это следующее.
.
Постскриптум
Для классов нового стиля см. другие ответы, в которых используется super()
Но super() работает только для классов нового стиля
В любом случае, этот ответ может быть полезен, по крайней мере, но только для классических классов.
.
Когда интерпретатор приходит к команде for n in A.gen(self):
, он должен найти функцию A.gen.
Обозначение A.gen
не означает, что объект A.gen находится внутри объекта A.
Объект A.gen является SOMEWHERE в памяти, и интерпретатор будет знать, где его найти, получив необходимую информацию (адрес) из A.__dict__['gen']
, в которой A.__dict__
- пространство имен А.
Итак, поиск объекта функции A.gen в памяти требует поиска в A.__ dict __
Но для выполнения этого поиска интерпретатор должен сначала найти объект A.
Итак, когда он прибывает в команду for n in A.gen(self):
, он сначала ищет, если идентификатор A
входит в число локальных идентификаторов, то есть он ищет строку "A" в локальном пространстве имен функции (из которой я не знаю названия).
Поскольку это не так, интерпретатор выходит за пределы функции и ищет этот идентификатор на уровне модуля, в глобальном пространстве имен (который globals())
В этот момент может быть, что глобальное пространство имен будет содержать сотни или тысячи имен атрибутов, среди которых можно выполнить поиск для "A".
Однако A имеет очень мало атрибутов: ключи __dict__
- это только _ _ модуль _ ',' gen 'и' _ doc _ '(чтобы увидеть это, сделайте print A.__dict__
)
Поэтому было бы очень жаль, что небольшой поиск строки 'gen' в A._dict _ должен выполняться после поиска среди сотен элементов в пространстве имен словарей globals() уровня модуля.
.
Вот почему я предлагаю другой способ заставить интерпретатора найти функцию A.gen
class A:
def gen(self):
yield 1
yield 2
class BB(A):
def gen(self):
yield 3
for n in self.__class__.__bases__[0].gen(self):
yield n
bb = BB()
print list(bb.gen()) # prints [3, 1, 2]
self._class _ - это класс, из которого был создан экземпляр, т.е. он Bu
self._class _._ bases _ - это кортеж, содержащий базовые классы Bu
В настоящее время в этом кортеже есть только один элемент, поэтому self._class _._ bases_ [0] A
__class__
и __bases__
- это имена специальных атрибутов, которые не указаны в _dict _;
Фактически _class _, _bases _ и _dict _ являются специальными атрибутами аналогичного характера, они являются атрибутами, предоставляемыми Python, см.:
http://www.cafepy.com/article/python_attributes_and_methods/python_attributes_and_methods.html
.
Ну, я имею в виду, что в конце концов, есть несколько элементов в self._class _ и в self._class _._ bases _, поэтому разумно что последующие поиски в этих объектах, чтобы наконец найти способ доступа к A.gen, будут быстрее, чем поиск в "gen" в глобальном пространстве имен, если этот содержит сотни элементов.
Возможно, это попытка сделать слишком большую оптимизацию, может быть, и нет.
Этот ответ в основном заключается в предоставлении информации о лежащих в основе предполагаемых механизмах, которые я лично считаю интересными.
.
Изменить
Вы можете получить то же, что и ваш код, с более сжатой инструкцией
class A:
def gen(self):
yield 1
yield 2
class Bu(A):
def gen(self):
yield 3
for n in A.gen(self):
yield n
b = Bu()
print 'list(b.gen()) ==',list(b.gen())
from itertools import chain
w = chain(iter((3,)),xrange(1,3))
print 'list(w) ==',list(w)
производит
list(b.gen()) == [3, 1, 2]
list(w) == [3, 1, 2]