Как с классом в Python определить функцию для печати каждого экземпляра класса в формате, определенном в функции?
Попытка сделать что-то со всеми экземплярами класса почти всегда является ошибкой и признаком того, что вы еще не привыкли организовывать свои объекты с помощью структур данных. При правильных структурах данных правильная операция - это что-то вроде «делать [что-то] со всеми элементами этой структуры данных», а не «делать [что-то] со всеми экземплярами этого класса».






Как и почти все другие объектно-ориентированные языки, храните все экземпляры класса в какой-либо коллекции.
Вы можете попробовать такие вещи.
class MyClassFactory( object ):
theWholeList= []
def __call__( self, *args, **kw ):
x= MyClass( *args, **kw )
self.theWholeList.append( x )
return x
Теперь ты можешь это сделать.
object= MyClassFactory( args, ... )
print MyClassFactory.theWholeList
Не совсем правильно. Некоторые языки предлагают доступ к своей объектной памяти. В этих языках, например Smalltalk и Ruby, вы запрашиваете у класса все его экземпляры. (На самом деле, я удивлен, что Python этого тоже не предлагает.)
@ Адриан Кун: см. Сообщение @ NXC о возможностях Smalltalk allInstances. Может, Руби - последний противник?
Лично я бы предпочел не видеть, как интерпретатор несет дополнительные накладные расходы, всегда предоставляя что-то, что не всегда требуется, особенно когда это - как показано - тривиально легко реализовать, когда это необходимо.
В этом случае я вижу два варианта:
import gc
for obj in gc.get_objects():
if isinstance(obj, some_class):
dome_something(obj)
Недостатком этого является то, что он очень медленный, когда у вас много объектов, но работает с типами, над которыми вы не можете контролировать.
from collections import defaultdict
import weakref
class KeepRefs(object):
__refs__ = defaultdict(list)
def __init__(self):
self.__refs__[self.__class__].append(weakref.ref(self))
@classmethod
def get_instances(cls):
for inst_ref in cls.__refs__[cls]:
inst = inst_ref()
if inst is not None:
yield inst
class X(KeepRefs):
def __init__(self, name):
super(X, self).__init__()
self.name = name
x = X("x")
y = X("y")
for r in X.get_instances():
print r.name
del y
for r in X.get_instances():
print r.name
В этом случае все ссылки сохраняются как слабые ссылки в списке. Если вы часто создаете и удаляете много экземпляров, вам следует очистить список слабых ссылок после итерации, иначе будет много мусора.
Другая проблема в этом случае заключается в том, что вы должны обязательно вызвать конструктор базового класса. Вы также можете переопределить __new__, но при создании экземпляра используется только метод __new__ первого базового класса. Это также работает только с типами, которые находятся под вашим контролем.
Редактировать: метод печати всех экземпляров в соответствии с определенным форматом оставлен в качестве упражнения, но в основном это просто вариация циклов for.
+1 за примеры кода и демо для слабых ссылок. Сборщик мусора интересен, но, вероятно, не так полезен для этого типа приложений.
Я знаю, но это последнее средство, если все остальное выйдет из строя. Может, стоило поставить как вариант 2.
IMO OP спрашивает о методе __str __ ().
Как бы вы расширили это, чтобы включить экземпляры дочерних классов?
Если у меня есть два класса, которые наследуются от KeepRefs, то кажется, что _refs является общим для этих двух классов. Это не то, что я хотел, и я не понимал, что наследование классов будет работать таким образом. Означает ли это, что необходимо реализовать KeepRefs как метакласс, чтобы каждый класс, который его использует, получил свой собственный _refs?
Очень красивый и полезный код, но у него есть большая проблема: список всегда больше и никогда не очищается, чтобы протестировать его, просто добавьте print(len(cls.__refs__[cls])) в конец метода get_instances. `@classmethod def get_instances (cls): cls .__ refs __ [cls] = [instance_ref for instance_ref in cls .__ refs __ [cls] \ if instance_ref () is not None] for instance_ref in cls .__ refs __ [cls]: instance = instance_ref ( ) yield instance print (len (cls .__ refs __ [cls])) `
Python не имеет эквивалента Smallktalk #allInstances, поскольку в архитектуре нет такой центральной таблицы объектов (хотя современные smalltalks на самом деле так не работают).
Как сказано на другом плакате, вы должны явно управлять коллекцией. Его предложение фабричного метода, поддерживающего реестр, является вполне разумным способом сделать это. Возможно, вы захотите что-нибудь сделать с слабые ссылки, чтобы вам не приходилось явно отслеживать удаление объекта.
Неясно, нужно ли вам печатать все экземпляры класса сразу или когда они инициализированы, или если вы говорите о классе, который вы контролируете, по сравнению с классом в сторонней библиотеке.
В любом случае я бы решил эту проблему, написав фабрику классов с использованием поддержки метаклассов Python. Если у вас нет контроля над классом, вручную обновите __metaclass__ для отслеживаемого класса или модуля.
См. http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html для получения дополнительной информации.
Вы захотите создать статический список в своем классе и добавить weakref к каждому экземпляру, чтобы сборщик мусора мог очистить ваши экземпляры, когда они больше не нужны.
import weakref
class A:
instances = []
def __init__(self, name=None):
self.__class__.instances.append(weakref.proxy(self))
self.name = name
a1 = A('a1')
a2 = A('a2')
a3 = A('a3')
a4 = A('a4')
for instance in A.instances:
print(instance.name)
Можно ли распечатать все экземпляры класса без weakref или любого другого специального модуля? Что произойдет, если он будет написан как «self.name = name \ n self .__ class __. Instance.append (name) \ n»?
Очень красивый и полезный код, но у него есть большая проблема: список всегда больше, и он никогда не очищается, чтобы протестировать его, просто добавьте print(len(cls.__refs__[cls])) в конце метода get_instances.
Вот исправление для метода get_instances:
__refs__ = defaultdict(list)
@classmethod
def get_instances(cls):
refs = []
for ref in cls.__refs__[cls]:
instance = ref()
if instance is not None:
refs.append(ref)
yield instance
# print(len(refs))
cls.__refs__[cls] = refs
или, в качестве альтернативы, это можно сделать с помощью WeakSet:
from weakref import WeakSet
__refs__ = defaultdict(WeakSet)
@classmethod
def get_instances(cls):
return cls.__refs__[cls]
Вам не нужно НИЧЕГО импортировать! Просто используйте «я». Вот как ты это делаешь
class A:
instances = []
def __init__(self):
self.__class__.instances.append(self)
@classmethod
def printInstances(cls):
for instance in cls.instances:
print(instance)
A.printInstances()
Все очень просто. Модули или библиотеки не импортированы
Спасибо, но работает только для классов, которые вы создаете. Если вы хотите сделать это для классов, которые вы импортируете из существующего пакета, вам придется обернуть их этой функцией. Это выполнимо, но вам нужно, чтобы весь ваш код создавал их, используя класс-оболочку, а не исходный.
В моем проекте я столкнулся с аналогичной проблемой и нашел простое решение, которое также может сработать для вас при перечислении и печати экземпляров вашего класса. Решение работало без сбоев в Python версии 3.7; дал частичные ошибки в Python версии 3.5.
Я скопирую и вставлю соответствующие блоки кода из моего недавнего проекта.
```
instances = []
class WorkCalendar:
def __init__(self, day, patient, worker):
self.day = day
self.patient = patient
self.worker= worker
def __str__(self):
return f'{self.day} : {self.patient} : {self.worker}'
В Python метод __str__, в конце концов, определяет, как объект будет интерпретироваться в его строковой форме. Я добавил : между фигурными скобками, это полностью мое предпочтение для чтения типа "Pandas DataFrame". Если вы примените эту небольшую функцию __str__, вы не увидите некоторые машиночитаемые описания типов объектов, что не имеет смысла для человеческого глаза. После добавления этой функции __str__ вы можете добавлять свои объекты в список и распечатывать их по своему усмотрению.
appointment= WorkCalendar("01.10.2020", "Jane", "John")
instances.append(appointment)
Для печати ваш формат в __str__ будет работать по умолчанию. Но также можно вызывать все атрибуты по отдельности:
for instance in instances:
print(instance)
print(instance.worker)
print(instance.patient)
Для подробного прочтения вы можете посмотреть источник: https://dbader.org/blog/python-repr-vs-str
Это сбивающий с толку вопрос. Вам нужна функция метода, которая форматирует их все одинаково? Или вам нужна коллекция, содержащая все экземпляры?