lbolla / EMpy

Electromagnetic Python
MIT License
194 stars 83 forks source link

EMpy.utils.Multilayer() needs .copy() method #18

Closed demisjohn closed 7 years ago

demisjohn commented 7 years ago

Since underlying structure is List, assignments are by reference. The Multilayer.copy() method is needed to prevent alteration of assigned objects.

Eg.:

>>> iso_layers = EMpy.utils.Multilayer()
< append layers to iso_layers >
>>> print(iso_layers)
Multilayer
----------
0: lyr 0, isotropic, thickness: 1.10352e-07
1: lyr 1, isotropic, thickness: 1.30269e-07
2: lyr 2, isotropic, thickness: 1.10352e-07

>>> test =iso_layers

>>> print(test)
Multilayer
----------
0: lyr 0, isotropic, thickness: 1.10352e-07
1: lyr 1, isotropic, thickness: 1.30269e-07
2: lyr 2, isotropic, thickness: 1.10352e-07

test & iso_layers are the same.

>>> iso_layers.pop(0)  # remove 1st layer
<EMpy.utils.Layer object at 0x1176d0da0>

>>> print(iso_layers)
Multilayer
----------
0: lyr 1, isotropic, thickness: 1.30269e-07
1: lyr 2, isotropic, thickness: 1.10352e-07

>>> print(test)
Multilayer
----------
0: lyr 1, isotropic, thickness: 1.30269e-07
1: lyr 2, isotropic, thickness: 1.10352e-07

test was also updated. Would usually use test =iso_layers.copy() to prevent this, but the method doesn't exist.

demisjohn commented 7 years ago

workaround is to use module copy, like so:

from copy import deepcopy

test = deepcopy( iso_layers )

now changes to iso_layers don't also update test.

lbolla commented 7 years ago

MultiLayer behaves as a list, even in the fact that it is mutable (i.e. assigning it to another variable does not copy it). It also, like list, does not provide a copy() method. Instead, you can simply copy the elements of a MultiLayer (and a list) using the slice operator:

test = iso_layers[:]

would create a brand new MultiLayer called test. Full example:

import EMpy.utils as U
import EMpy.materials as M

iso_layers = U.Multilayer()
iso_layers.append(U.Layer(M.Air, 1))
iso_layers.append(U.Layer(M.Si, 2))

print iso_layers

test = iso_layers[:]
iso_layers.pop(0)

print 'ORIG', iso_layers
print 'COPY', test
demisjohn commented 7 years ago

Thanks for the native workaround, I'll use that instead.

On my python 3.5.1, Lists do appear to have a .copy() method, which is why I suggested it:

>>> dir([1,2])
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

>>> a = [1,2].copy()
lbolla commented 7 years ago

It seems like list.copy() was introduced in Python 3.3 (https://bugs.python.org/issue10516). I'd stick with the slice operator, because it's more portable.