[Python]Подсчет числа экземпляров класса
От: slava_phirsov Россия  
Дата: 21.06.10 11:29
Оценка:
Доброго времени суток всем читающим.

Задача: нужно подсчитывать число созданных экземпляров класса (а также, например, сохранять экземпляры в список, что-то вроде такого):

class Foo:
  INSTANCES = 0
  def __init__(self):
    self.INSTANCES += 1
  # В деструкторе, понятно, число экземпляров надо уменьшить, но на этот момент мы забъем


И классов таких должно быть несколько(и для каждого свой отдельный счетчик). И при этом чтобы поменьше оверхеда. Поскольку декораторы классов в Python 2.5 не реализованы (Е.М.Н.И.П, они еще нигде не реализованы), то придумалось вот такое:

def incr_count(constructor):
  counter_name = 'INSTANCES'
  def _constructor_wrapper_(self, *args, **kwargs):
    if not hasattr(self.__class__, counter_name):
      setattr(self.__class__, counter_name, 1)
    else:
      setattr(self.__class__, counter_name, getattr(self.__class__, counter_name) + 1)
    constructor(self, *args, **kwargs)
  return _constructor_wrapper_

class Foo:
  @incr_count
  def __init__(self):
    ...
  # И еще потом не забыть декорировать деструктор

class Bar:
  @incr_count
  def __init__(self):
    ...


Но как-то не очень нравится, может у кого-нибудь есть более кошерное решение?
Заранее благодарю.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re: [Python]Подсчет числа экземпляров класса
От: Аноним  
Дата: 21.06.10 11:39
Оценка: +2
Здравствуйте, slava_phirsov, Вы писали:
Попахивает метаклассами
Re: [Python]Подсчет числа экземпляров класса
От: Temoto  
Дата: 21.06.10 11:49
Оценка:
_>Задача: нужно подсчитывать число созданных экземпляров класса (а также, например, сохранять экземпляры в список, что-то вроде такого):

_>И классов таких должно быть несколько(и для каждого свой отдельный счетчик). И при этом чтобы поменьше оверхеда. Поскольку декораторы классов в Python 2.5 не реализованы (Е.М.Н.И.П, они еще нигде не реализованы), то придумалось вот такое:


Метаклассы реализованы.

_>
_>class Foo:
_>  @incr_count
_>  def __init__(self):
_>    ...
_>  # И еще потом не забыть декорировать деструктор
_>


И ещё потом не забыть, что деструктор могут и не вызвать в случае циклических ссылок.

_>Но как-то не очень нравится, может у кого-нибудь есть более кошерное решение?

_>Заранее благодарю.

Нужно изменить задачу. Точнее, просто решать задачу, а не изыскивать языковые хитрости. Чтоб не было этого бреда с подсчётом экземпляров.

Скажите что на самом деле нужно сделать.
Re[2]: [Python]Подсчет числа экземпляров класса
От: slava_phirsov Россия  
Дата: 21.06.10 11:59
Оценка:
Здравствуйте, Temoto, Вы писали:

T>Метаклассы реализованы.


Если ты о __metaclass__(name, bases, dict), то оно вызывается при создании, т.е. при удалении все равно придется мудрить с del

T>И ещё потом не забыть, что деструктор могут и не вызвать в случае циклических ссылок.

Значит объект не будет удален, значит счетчик ссылок не будет декрементирован. Все логично... Конечно, если объект будет сохраняться во внутреннем списке, то возникнет загогулина в виде циклической ссылки, но на то есть weakref, так что и тут не бином Ньютона.

T>Нужно изменить задачу. Точнее, просто решать задачу, а не изыскивать языковые хитрости. Чтоб не было этого бреда с подсчётом экземпляров.


- Ты, наверное, из Microsoft? — спросил пастух.
— Да — ответил яппи — А как ты догадался?
— Ты явился непрошенным, и на вопрос, который я тебе не задавал, дал мне ответ, который я и без тебя знал. И еще ты ни черта не смыслишь в моем бизнесе.

Д.Платт "Софт — отстой"

Ты уж извини, навеяло...
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[3]: [Python]Подсчет числа экземпляров класса
От: Temoto  
Дата: 21.06.10 12:17
Оценка:
T>>Метаклассы реализованы.

_>Если ты о __metaclass__(name, bases, dict), то оно вызывается при создании, т.е. при удалении все равно придется мудрить с del


Да, об этом. Только не с __del__ придётся мудрить, а с логикой выше, которая эти классы создаёт. Потому что вызов __del__ не гарантирован.

T>>И ещё потом не забыть, что деструктор могут и не вызвать в случае циклических ссылок.

_>Значит объект не будет удален, значит счетчик ссылок не будет декрементирован. Все логично... Конечно, если объект будет сохраняться во внутреннем списке, то возникнет загогулина в виде циклической ссылки, но на то есть weakref, так что и тут не бином Ньютона.

И при неиспользовании weakref объект таки будет удалён, а деструктор не позовут.

T>>Нужно изменить задачу. Точнее, просто решать задачу, а не изыскивать языковые хитрости. Чтоб не было этого бреда с подсчётом экземпляров.


_>Ты уж извини, навеяло...


Без проблем. Вспомните задачу — будет интереснее.
Re[4]: [Python]Подсчет числа экземпляров класса
От: slava_phirsov Россия  
Дата: 21.06.10 12:45
Оценка:
Здравствуйте, Temoto, Вы писали:

T>Потому что вызов __del__ не гарантирован.


Note: del x doesn’t directly call x.__del__() — the former decrements the reference count for x by one, and the latter is only called when x‘s reference count reaches zero. Some common situations that may prevent the reference count of an object from going to zero include: circular references between objects (e.g., a doubly-linked list or a tree data structure with parent and child pointers); a reference to the object on the stack frame of a function that caught an exception (the traceback stored in sys.exc_traceback keeps the stack frame alive); or a reference to the object on the stack frame that raised an unhandled exception in interactive mode (the traceback stored in sys.last_traceback keeps the stack frame alive). The first situation can only be remedied by explicitly breaking the cycles; the latter two situations can be resolved by storing None in sys.exc_traceback or sys.last_traceback. Circular references which are garbage are detected when the option cycle detector is enabled (it’s on by default), but can only be cleaned up if there are no Python-level __del__() methods involved. Refer to the documentation for the gc module for more information about how __del__() methods are handled by the cycle detector, particularly the description of the garbage value.


Пока что не увидел в перечисленном криминала из серии "объект удаляется, а деструктор не вызывается". Скорее все наоборот, и в точности соответствует ожиданиям: объект удаляется — был вызван __del__ — число ссылок уменьшаем, не удаляется — не был вызван — не уменьшаем. Причины неудаления перечислены.

T>И при неиспользовании weakref объект таки будет удалён, а деструктор не позовут.


Еще раз: допустим, я сохраняю ссылку на объект в контейнере, являющемся атрибутом класса этого самого объекта. Если этот контейнер — weakref.WeakValueDictionary, то перед вызовом __del__ ссылка на объект будет удалена. Если же контейнер — список или словарь, то на объект останется strong reference, со всеми вытекающими последствиями... Во-вторых, когда это объект будет удален без вызова __del__? Когда приложение закончено? Да в принципе, тогда и мне оно не особо нужно, мне бы в процессе, пока оно крутится...
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[4]: [Python]Подсчет числа экземпляров класса
От: slava_phirsov Россия  
Дата: 21.06.10 12:50
Оценка:
Здравствуйте, Temoto, Вы писали:

_>>Ты уж извини, навеяло...


T>Без проблем. Вспомните задачу — будет интереснее.


Прошу извинить за резкость. "Прошу меня извинить — я был нетрезв" (с) "Асса"
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[5]: [Python]Подсчет числа экземпляров класса
От: Temoto  
Дата: 21.06.10 14:22
Оценка:
T>>И при неиспользовании weakref объект таки будет удалён, а деструктор не позовут.

_>Еще раз: допустим, я сохраняю ссылку на объект в контейнере, являющемся атрибутом класса этого самого объекта. Если этот контейнер — weakref.WeakValueDictionary, то перед вызовом __del__ ссылка на объект будет удалена. Если же контейнер — список или словарь, то на объект останется strong reference, со всеми вытекающими последствиями... Во-вторых, когда это объект будет удален без вызова __del__? Когда приложение закончено? Да в принципе, тогда и мне оно не особо нужно, мне бы в процессе, пока оно крутится...


Да, верно, это я облапошился, из-за __del__ просто мусор собран не будет (что тоже не сахар). Извините.

Всё-таки, даже признав ошибку я настаиваю, что подсчитывать количество экземпляров это не типовая задача. Для поиска ошибок есть всякие heapy, guppy, dowser, objgraph; а "добрые дела ночью не делают".
Re[6]: [Python]Подсчет числа экземпляров класса
От: slava_phirsov Россия  
Дата: 22.06.10 06:23
Оценка:
Здравствуйте, Temoto, Вы писали:

T>Всё-таки, даже признав ошибку я настаиваю, что подсчитывать количество экземпляров это не типовая задача. Для поиска ошибок есть всякие heapy, guppy, dowser, objgraph; а "добрые дела ночью не делают".


Необязательно для поиска ошибок и необязательно подсчет числа экземпляров, как я уже говорил — например, сохранение объекта в контейнере. Вот надуманный пример из реальной жизни: пишем на основе PyGame игру тактический тренажер для спецподразделений МВД РФ "RP-75: The democracy maker". Миссия №1: действия оперативной группы при пресечении мелкого правонарушения — выброса мусора (прошу, товарищ капитан, понять меня правильно) мимо урны. Имеем сущности: сотрудник МВД, правонарушитель, урна, деревья, скамейки, окурки, бутылки. Экземпляры этих классов храним в группах (контейнерах pygame.sprite.Group). Можно при создании каждого экземпляра вручную заносить его в соответствующую группу (иногда и не одну, а сразу в несколько). Можно также слепить для каждой сущности фабричную функцию, которая заодно будет заносить экземпляр в нужные контейнеры. Можно поручить это конструктору. Или использовать подход, который я продемонстрировал в начальном посте. Или использовать еще что-то другое. Если бы была возможность декорирования классов (как это обсуждается, насколько я вижу, на python.org), то оно было бы, ИМХО, самое то. Хотя я не настаиваю, ведь девиз скриптовых языков: There-Is-More-Than-One-WayTo-Do-It, а наличие единственно верного решения (TM) — особенность другого хорошего языка программирования с забавным названием, состоящим в основном из арифметических символов
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[7]: [Python]Подсчет числа экземпляров класса
От: Temoto  
Дата: 22.06.10 08:43
Оценка:
T>>Всё-таки, даже признав ошибку я настаиваю, что подсчитывать количество экземпляров это не типовая задача. Для поиска ошибок есть всякие heapy, guppy, dowser, objgraph; а "добрые дела ночью не делают".

_>Необязательно для поиска ошибок и необязательно подсчет числа экземпляров, как я уже говорил — например, сохранение объекта в контейнере. Вот надуманный пример из реальной жизни: пишем на основе PyGame игру тактический тренажер для спецподразделений МВД РФ "RP-75: The democracy maker". Миссия №1: действия оперативной группы при пресечении мелкого правонарушения — выброса мусора (прошу, товарищ капитан, понять меня правильно) мимо урны. Имеем сущности: сотрудник МВД, правонарушитель, урна, деревья, скамейки, окурки, бутылки. Экземпляры этих классов храним в группах (контейнерах pygame.sprite.Group). Можно при создании каждого экземпляра вручную заносить его в соответствующую группу (иногда и не одну, а сразу в несколько). Можно также слепить для каждой сущности фабричную функцию, которая заодно будет заносить экземпляр в нужные контейнеры. Можно поручить это конструктору. Или использовать подход, который я продемонстрировал в начальном посте. Или использовать еще что-то другое. Если бы была возможность декорирования классов (как это обсуждается, насколько я вижу, на python.org), то оно было бы, ИМХО, самое то. Хотя я не настаиваю, ведь девиз скриптовых языков: There-Is-More-Than-One-WayTo-Do-It, а наличие единственно верного решения (TM) — особенность другого хорошего языка программирования с забавным названием, состоящим в основном из арифметических символов


Группы нужны только для рисования? (я в PyGame не разбираюсь, больше с Panda3d опыта имел)
Re[8]: [Python]Подсчет числа экземпляров класса
От: slava_phirsov Россия  
Дата: 22.06.10 08:59
Оценка:
Здравствуйте, Temoto, Вы писали:

T>Группы нужны только для рисования? (я в PyGame не разбираюсь, больше с Panda3d опыта имел)


Не только. Группа — это многоцелевой контейнер. С его помощью можно определять столкновения спрайтов (или, если угодно, сущностей), вызывать для всех спрайтов группы методы update или kill, ну ит.п. Спрайт может быть членом нескольких групп одновременно, причем метод kill вычищает спрайт из всех групп, в которых он состоит. Почему для этой цели не использовались списки или weakref-словари, а изобрели спец.контейнер? Не знаю, возможно, была причина, а может просто так захотелось. Я этот пример просто так привел, прошу не воспринимать его буквально.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[9]: [Python]Подсчет числа экземпляров класса
От: Temoto  
Дата: 22.06.10 10:58
Оценка:
T>>Группы нужны только для рисования? (я в PyGame не разбираюсь, больше с Panda3d опыта имел)

_>Не только. Группа — это многоцелевой контейнер. С его помощью можно определять столкновения спрайтов (или, если угодно, сущностей), вызывать для всех спрайтов группы методы update или kill, ну ит.п. Спрайт может быть членом нескольких групп одновременно, причем метод kill вычищает спрайт из всех групп, в которых он состоит. Почему для этой цели не использовались списки или weakref-словари, а изобрели спец.контейнер? Не знаю, возможно, была причина, а может просто так захотелось. Я этот пример просто так привел, прошу не воспринимать его буквально.


В общем, специализорованный список (может скорее даже набор). В Panda3d свои велосипеды на каждом шагу потому что ниже всё реализовано на плюсах. Жертвуют питоничностью ради скорости.

Я б добавлял объекты в группы явно. В частности, это подходит под питонячий дзен explicit is better than implicit, но этот дзен что русские законы: как дышло, куда повернёшь, туда и вышло.

А вот удаление из групп при удалении объекта, это, действительно, было бы хорошо автоматизировать. Обычно делают "cleanup метод" close, который нужно вызывать явно. Вот в нём я бы удалил объект из всех групп. А close вызывал бы явно или в group.kill или где ещё бывает "смерть объекта"
Re[3]: [Python]Подсчет числа экземпляров класса
От: Temoto  
Дата: 22.06.10 11:00
Оценка:
T>>Метаклассы реализованы.

_>Если ты о __metaclass__(name, bases, dict), то оно вызывается при создании, т.е. при удалении все равно придется мудрить с del


Кстати, ещё про это.

При создании можно добавить __del__. В принципе, подход с метаклассом аналогичен добавлению mixin. У нас же множественное наследование, блин.
Re[4]: [Python]Подсчет числа экземпляров класса
От: slava_phirsov Россия  
Дата: 22.06.10 11:41
Оценка:
Здравствуйте, Temoto, Вы писали:

T>При создании можно добавить __del__. В принципе, подход с метаклассом аналогичен добавлению mixin. У нас же множественное наследование, блин.


Это да, но: __metaclass__ у класса только один, т.е. при добавлении одного объекта (в примере, который см.ниже) в несколько групп для каждой комбинации групп придется делать отдельный __metaclass__; множественное наследование дает оверхед: требуется не только писать списки родителей класса, но и принудительно вызывать в конструкторе конструктор каждого родителя. С тем, что explicit лучше, чем implicit — ну, когда как. Это все тот же Платтовский вопрос: "Сколько процентов программистов ездят на машине с ручной коробкой передач, и сколько процентов юзеров ездят на машине с ручной коробкой передач." Контроль за ситуацией это хорошо, но иногда "контроль всего" оборачивается оверхедом.

ИМХО:
class Policman:
  @belongs_to(GROUND_OBJECTS, HUMANS, POLICE_PROPERTY)
  def __init__(self):
    ......

лучше, чем:
class Policman(GroundObjects, Humans, PoliceProperty):
  def __init__(self):
    # Не забываем, что при изменении сигнатуры конструкторов базовых классов
    # нам понадобится изменять и вызовы во всех порожденных классах
    GroundObjects.__init__(self)
    Humans.__init__(self)
    PoliceProperty.__init__(self)
    .......


1-й пример — с использованием декораторов и групп-глобальных переменных (да, да, я знаю, глобальные переменные — зло, но иногда и они бывают нужны; в Python есть глобальные переменные в модулях sys или os — и ничего, живем). Второй пример — с использованием mixin классов.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[7]: [Python]Подсчет числа экземпляров класса
От: Аноним  
Дата: 22.06.10 12:47
Оценка:
Здравствуйте, slava_phirsov, Вы писали:

_>(как это обсуждается, насколько я вижу, на python.org), то оно было бы, ИМХО, самое то. Хотя я не настаиваю, ведь девиз скриптовых языков: There-Is-More-Than-One-WayTo-Do-It, а наличие единственно верного решения (TM) — особенность другого хорошего языка программирования с забавным названием, состоящим в основном из арифметических символов


Ересь! Вы утеряли истинный путь, о заблудший коллега, и потому погрязли в терниях проблем!
Срочно, немедленно откройте консоль Питона, введите мантру "import this" и читайте до просветления! Особое внимание обратите на строфы 2, 3 и 13. И свет истины снова озарит ваши пути, и трудности расточатся как дым!

Re[5]: [Python]Подсчет числа экземпляров класса
От: Temoto  
Дата: 22.06.10 13:09
Оценка:
T>>При создании можно добавить __del__. В принципе, подход с метаклассом аналогичен добавлению mixin. У нас же множественное наследование, блин.

_>Это да, но: __metaclass__ у класса только один, т.е. при добавлении одного объекта (в примере, который см.ниже) в несколько групп для каждой комбинации групп придется делать отдельный __metaclass__; множественное наследование дает оверхед: требуется не только писать списки родителей класса, но и принудительно вызывать в конструкторе конструктор каждого родителя. С тем, что explicit лучше, чем implicit — ну, когда как. Это все тот же Платтовский вопрос: "Сколько процентов программистов ездят на машине с ручной коробкой передач, и сколько процентов юзеров ездят на машине с ручной коробкой передач." Контроль за ситуацией это хорошо, но иногда "контроль всего" оборачивается оверхедом.


_>ИМХО:

_>
_>class Policman:
^ в Python2 опечатка: нет наследования от object
_>  @belongs_to(GROUND_OBJECTS, HUMANS, POLICE_PROPERTY)
_>  def __init__(self):
_>    ......
_>

_>лучше, чем:
_>
_>class Policman(GroundObjects, Humans, PoliceProperty):
_>  def __init__(self):
_>    # Не забываем, что при изменении сигнатуры конструкторов базовых классов
_>    # нам понадобится изменять и вызовы во всех порожденных классах
_>    GroundObjects.__init__(self)
_>    Humans.__init__(self)
_>    PoliceProperty.__init__(self)
_>    .......
_>


_>1-й пример — с использованием декораторов и групп-глобальных переменных (да, да, я знаю, глобальные переменные — зло, но иногда и они бывают нужны; в Python есть глобальные переменные в модулях sys или os — и ничего, живем). Второй пример — с использованием mixin классов.


Там должен быть один super(PolicMan, self).__init__() и всё.
Хотя, подозреваю, что PyGame так же, как и Panda3d, весь построен на old-style классах и супер-фокус не пройдёт, да?

Про глобальные переменные ерунда, в вашем примере это константа согласно PEP-8.

Не то чтоб я продвигал какой-то один из этих способов. С belongs_to тоже всё отлично.
Re[6]: [Python]Подсчет числа экземпляров класса
От: slava_phirsov Россия  
Дата: 23.06.10 05:51
Оценка:
Здравствуйте, Temoto, Вы писали:

T>Там должен быть один super(PolicMan, self).__init__() и всё.

T>Хотя, подозреваю, что PyGame так же, как и Panda3d, весь построен на old-style классах и супер-фокус не пройдёт, да?

Да нет, вроде Sprite — потомок object. Однако... Вот пример, который не прокатил То ли лыжи не едут....
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.