Лучший способ связать два разных, не унаследованных параметра / переменных класса

#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. что ж, это все объясняет. Моя голова все еще завершает ООП после многих лет функционального программирования, поэтому некоторые базовые понятия все еще сбивают с толку, но это решает проблему. Спасибо!