#python #python-3.x #class
#python #python-3.x #класс
Вопрос:
Это моя первая попытка ООП, исходящая из опыта функционального программирования, поэтому у меня возникли небольшие проблемы с проектированием связанных / связанных объектов. Я пытаюсь создать простую программу моделирования процесса, которая динамически решает уравнения баланса массы и энергии, и у меня возникают проблемы при попытке заставить два разных класса взаимодействовать друг с другом.
Я объявил два основных класса для описания моей проблемы:
class Stream:
""" class that describes a fluid flow, with properties such as mass flow, temperature, pressure, etc."""
def __init__(self, massflow=1, temperature=0):
self.m = massflow
self.temperature = temperature
class Equip:
""" class that describes a process equipment, such as a heater """
def __init__(self):
self.StreamIn = Stream()
self.StreamOut = Stream()
def heating(self):
"""example method"""
self.StreamOut.temperature = self.StreamIn.temperature 100
Пока все хорошо. Каждое оборудование может иметь несколько входящих или исходящих потоков, но для простоты я пробую с одним. Кроме того, каждый поток соединяет одно оборудование с другим.
Изначально я создал 2 экземпляра Equip и «физически» соединил их с потоком, поэтому у меня было бы что-то вроде:
heater[1] = Equip()
heater[2] = Equip()
eq[2].StreamIn = eq[1].StreamOut
heater[1].heating()
eq[2].StreamIn = eq[1].StreamOut
Это «работает», но это кажется… неоптимально. Каждый раз, когда я изменяю свойства потока, мне придется использовать это назначение, и я бы сохранил «копию» данных потока внутри eq [1] и eq [2] . Объект stream довольно сложный и имеет несколько вычислений, поэтому я бы хотел избежать его дублирования, если это возможно.
Альтернативой может быть изменение класса Equip для хранения только индекса потоков, а не самих потоков.
class Equip:
""" class that describes a process equipment, such as a heater """
def __init__(self, i_in, i_out):
self.i_in = i_in
self.i_out = i_out
def heating(self, StreamIn, StreamOut):
"""example method"""
StreamOut.temperature = StreamIn.temperature 100
eq[1] = Equip(0, 1)
eq[2] = Equip(1, 2)
s[0] = Stream()
s[1] = Stream()
s[2] = Stream()
i_in = eq[1].i_in
i_out = eq[1].i_out
eq[1].heating(s[i_in], s[i_out])
Опять же, это «работает», но не только синтаксис более раздражает, если я создаю какие-либо другие методы в Equip, мне придется передавать поток каждый раз. Кроме того, когда я добавляю несколько потоков ввода / вывода в оборудование, мне пришлось бы переписать весь код вместо метода.
Лучшим решением, которое я нашел до сих пор, было бы создать класс StreamList и переписать метод нагрева, чтобы использовать весь список s
...
def heating(self, StreamList):
"""example method"""
StreamList[i_out].temperature = StreamList[i_in].temperature 100
...
...
eq[1].heating(s)
Теперь код стал чище и кажется более гибким для будущих изменений в классах, но я не знаю, повлияет ли передача всего списка потоков на скорость вычислений (по сравнению с передачей одного экземпляра потока). В итеративной программе с десятками устройств, сотнями потоков, каждый с разными свойствами жидкости и независимыми вычислениями, выполняемыми каждый раз при изменении свойства, любой выигрыш в скорости приветствуется.
В идеале лучшим решением было бы использовать разные экземпляры Equip для «совместного использования» экземпляра Stream, и каждое изменение в этом потоке отражалось бы на обоих устройствах. Возможно ли это вообще? Мне не хватает какой-то скрытой функциональности, чтобы как бы «связать» эти две переменные? В противном случае, можете ли вы предложить другой подход?
Спасибо!
Комментарии:
1. Почему вы работаете с индексами? Кроме того, если вы создаете экземпляр a
Stream
вне класса и передаете один и тот же экземпляр нескольким классам, все они будут указывать на один и тот же объект. В вашем первом примере второйeq[2].StreamIn = eq[1].StreamOut
полностью избыточен.2. О, выполнив уравнение [2] . StreamIn = eq[1]. streamOut они уже обрабатываются как один и тот же объект? Я думал, что, делая это, я создам копию eq[1] . Поток внутри eq [2]. StreamIn, аналогично тому, как работает a = b, если a, b — числа. Является ли это поведение эксклюзивным для классов?
Ответ №1:
Я думаю, что вам здесь не хватает изменчивости. Если вы передаете функции, классу или методу изменяемый объект, он передаст ссылку на тот же объект, поэтому нет необходимости переназначать.
class Foo:
pass
# create a mutable instance of Foo
foo = Foo()
# add an attribute to the instance
foo.bar = 5
def baz(a: Foo):
a.bar = 10
baz(foo)
# what do you expect to print here?
print(foo.bar)
Если бы вы догадались 10
, вы были бы правы. Это связано с тем, что объекты в python изменяемы по умолчанию.
Теперь к вашему примеру.
# As minimal as possible
class Stream:
def __init__(self, temperature=0):
self.temperature = temperature
class Equip:
# takes 2 instances of Stream as arguments
def __init__(self, stream_in: Stream, stream_out: Stream):
self.stream_in = stream_in
self.stream_out = stream_out
def heating(self):
# sets the temperature of instances assigned in the constructor
self.stream_out.temperature = self.stream_in.temperature 100
# create a list of 2 stream instances
streams = [Stream() for _ in range(2)]
# I'm making these refer to each other for example's sake
heater1 = Equip(streams[0], streams[1])
heater2 = Equip(streams[1], streams[0])
# this method will cause all references to streams[1]
# to increase by 100 more than streams[0] (100)
heater1.heating()
# this method will cause all references to streams[0]
# to increase by 100 more than streams[1] (200)
heater2.heating()
print(streams[0].temperature) # 200
print(streams[1].temperature) # 100
# And then we repeat
heater1.heating() # streams[1] to (300)
heater2.heating() # streams[0] to (400)
print(streams[0].temperature) # 400
print(streams[1].temperature) # 300
Как вы видите, они по-прежнему являются одним и тем же потоком внутри каждого из объектов.
Нет необходимости вручную «связывать» их.
Каждый из них ссылается на одно и то же.
Комментарии:
1. что ж, это все объясняет. Моя голова все еще завершает ООП после многих лет функционального программирования, поэтому некоторые базовые понятия все еще сбивают с толку, но это решает проблему. Спасибо!