Idf_MSequence - Syntactic Sugar work¶
Underlying Data structure of again¶
Let us open a small idf file and look at the underlying data structure.
# assume we have open an IDF file called idf
# let us add three construction objects to it
idf.newidfobject('construction'.upper(), Name='C1')
idf.newidfobject('construction'.upper(), Name='C2')
idf.newidfobject('construction'.upper(), Name='C3')
constructions = idf.idfobjects['construction'.upper()]
print constructions
[
CONSTRUCTION,
C1; !- Name
,
CONSTRUCTION,
C2; !- Name
,
CONSTRUCTION,
C3; !- Name
]
We know that constructions us just syntactic sugar around the underlying data structure. Let us call the underlying data structure real_constructions
# set real_constructions
real_constructions = = idf.model.dt['construction'.upper()]
print real_constructions
[['CONSTRUCTION', 'C1'], ['CONSTRUCTION', 'C2'], ['CONSTRUCTION', 'C3']]
real_constructions -> the underlying data structure
constructions -> syntactic sugar for real_constructions
So any changes made in constructions should reflected in constructions. Let us test this out.
constructions[0].Name = 'New C1'
print constructions
[
CONSTRUCTION,
New C1; !- Name
,
CONSTRUCTION,
C2; !- Name
,
CONSTRUCTION,
C3; !- Name
]
print real_constructions
[['CONSTRUCTION', 'New C1'], ['CONSTRUCTION', 'C2'], ['CONSTRUCTION', 'C3']]
Even though we made the change only in constructions, we can see the
changes in both constructions and real_constructions. Ep_Bunch
takes care of this for us.
print 'type for constructions', type(constructions)
type for constructions <type 'list'>
since constructions is a list, we can do all the list operations on it. Let us try some of them:
constructions.pop(0)
CONSTRUCTION,
New C1; !- Name
print constructions
[
CONSTRUCTION,
C2; !- Name
,
CONSTRUCTION,
C3; !- Name
]
That makes sense. We poped the first item in the list and now we have only two items.
Is this change reflected in real_constructions ?
print real_constructions
[['CONSTRUCTION', 'New C1'], ['CONSTRUCTION', 'C2'], ['CONSTRUCTION', 'C3']]
Dammit !! Why not ?
We still have 3 items in real_constructions and 2 items in constructions
print 'type for constructions', type(constructions)
print 'id of constructions', id(constructions)
print 'type for real_constructions', type(constructions)
print 'id of real_constructions', id(real_constructions)
type for constructions <type 'list'>
id of constructions 4576898440
type for real_constructions <type 'list'>
id of real_constructions 4535436208
- Both
constructions
andreal_constructions
are lists. - But looking at their ids, it is clear that they are two different lists.
- poping an item in one list will not pop it in the other list :-(
- In
constructions[0].Name =
"New C1"
we see changes to an item withinconstructions
is reflected withinreal_constructions
EpBunch
takes care of that connection- We are having problems with the list functions.
- we see that pop() does not work for us
- similarly the results of append(), insert(), sort() and reverse() in
constructions
will not be reflected inreal_constructions
This is how it works in eppy version 0.5
We need to fix this. Now we describe how this problem was fixed.
constructions
should be a list-like wrapper around
real_constructions
. Python has an excellent data structure called
collections.MutableSequence
that works perfectly for this. Alex
Martelli has a great discussion of this in this stackoverflow thread
Overriding append method after inheriting from a Python
List
- So we make a class
eppy.idf_msequence.Idf_MSequence
that inherits formcollections.MutableSequence
constructions
is now an instance ofeppy.idf_msequence.Idf_MSequence
- reading the above stackoverflow thread and the code wihtin
eppy.idf_msequence.Idf_MSequence
should show you how it works - version of eppy higher than 0.5 will use
eppy.idf_msequence.Idf_MSequence
Let us take a look at how it works (in all versions of eppy newer than 0.5):
# using eppy version greater than 0.5
import sys
# pathnameto_eppy = 'c:/eppy'
pathnameto_eppy = '../../../'
sys.path.append(pathnameto_eppy)
from eppy import modeleditor
from eppy.modeleditor import IDF
iddfile = "../../../eppy/resources/iddfiles/Energy+V7_2_0.idd"
fname1 = "../../../eppy/resources/idffiles/V_7_2/smallfile.idf"
IDF.setiddname(iddfile)
idf = IDF(fname1)
idf.newidfobject('construction'.upper(), Name='C1')
idf.newidfobject('construction'.upper(), Name='C2')
idf.newidfobject('construction'.upper(), Name='C3')
constructions = idf.idfobjects['construction'.upper()]
print constructions
[
CONSTRUCTION,
C1; !- Name
,
CONSTRUCTION,
C2; !- Name
,
CONSTRUCTION,
C3; !- Name
]
real_constructions = idf.model.dt['construction'.upper()]
print real_constructions
[['CONSTRUCTION', 'C1'], ['CONSTRUCTION', 'C2'], ['CONSTRUCTION', 'C3']]
Shall we test pop(0)
here ?
constructions.pop(0)
CONSTRUCTION,
C1; !- Name
print constructions
[
CONSTRUCTION,
C2; !- Name
,
CONSTRUCTION,
C3; !- Name
]
print real_constructions
[['CONSTRUCTION', 'C2'], ['CONSTRUCTION', 'C3']]
Awesome !!! both constructions
and real_constructions
have the
same number of items
print type(constructions)
print type(real_constructions)
<class 'eppy.idf_msequence.Idf_MSequence'>
<type 'list'>
what kind of sorcery is this. How did that work. How does
Idf.Msequence
do this magic ? Let us look at that
link
in stackoverflow. The question raised in stackovverflow is:
I want to create a list that can only accept certain types. As such, I’m trying to inherit from a list in Python, and overriding the append() method like so: and there is a sample code after this.
Alex Martelli responds:
Not the best approach! Python lists have so many mutating methods that you’d have to be overriding a bunch (and would probably forget some).
Rather, wrap a list, inherit from collections.MutableSequence, and add
your checks at the very few “choke point” methods on which
MutableSequence relies to implement all others. Alex’s code follows
after this point. In eppy.idf_msequence
I have included Alex’s code.
Stop here and read through the stackoverflow link
Well ... you don’t really have to. It does go off on some tangents unrelated to what we do in eppy.
The strategy in eppy.idf_msequence.Idf_MSequence
is to have two
lists, list1 and list2. To play with this I made a simple class
TwoLists
. Here TwoLists
acts just like a list. Any operation
list operation on TwoLists
will result in a similar operation on
both list1 and list2. TwoLists
is not used in eppy, I simply use it
to flesh out how MutableSequence
can be used. I am going to play
with TwoLists
here to show you how cool it is :-)
from eppy.idf_msequence import TwoLists
twolists = TwoLists()
print twolists
list1 = [], list2 = []
twolists.append(5)
print twolists
list1 = [5], list2 = ['r_5']
twolists.append(dict(a=15))
print twolists
list1 = [5, {'a': 15}], list2 = ['r_5', "r_{'a': 15}"]
twolists.insert(1, 42)
print twolists
list1 = [5, 42, {'a': 15}], list2 = ['r_5', 'r_42', "r_{'a': 15}"]
twolists.pop(-1)
{'a': 15}
print twolists
list1 = [5, 42], list2 = ['r_5', 'r_42']
Isn’t that neat !! Idf_MSequence
works in a similar way. Out of
sheer laziness I am going to let you figure it out on your own. (ignore
Idf_MSequence_old
, since that went in the wrong direction)