masher
masher

Reputation: 4096

import from file data error in Python

I'm having some issues importing data from file in Python. I am quite new to Python, so my error is probably quite simple.

I am reading in 3 column, tab-delimited text files with no headers. I am creating 3 instances of the data file using three different datafiles.

I can see that each object is referencing a different memory location, so they are separate.

When I look at the data stored in each instance, each instance has the same contents, consisting of the three datafiles appended to each other.

What have I done wrong?

The class to read in the data is:

class Minimal:

    def __init__(self, data=[]):
        self.data = data

    def readFile(self, filename):
        f = open(filename, 'r')

        for line in f:
            line = line.strip()
            columns = line.split()
            #creates a list of angle, intensity and error and appends it to the diffraction pattern
            self.data.append( [float(columns[0]), float(columns[1]), float(columns[2])] )
        f.close()

    def printData(self):
        for dataPoint in self.data:
            print str(dataPoint)

The datafiles look like:

1   4   2
2   5   2.3
3   4   2
4   6   2.5
5   8   5
6   10  3

The program I am using to actually create the instances of Minimal is:

from minimal import Minimal

d1 = Minimal()
d1.readFile("data1.xye")

d2 = Minimal()
d2.readFile("data2.xye")

d3 = Minimal()
d3.readFile("data3.xye")


print "Data1"
print d1
d1.printData()

print "\nData2"
print d2
d2.printData()

print "\nData3"
print d3
d3.printData()

The output is:

Data1
<minimal.Minimal instance at 0x016A35F8>
[1.0, 4.0, 2.0]
[2.0, 5.0, 2.3]
[3.0, 4.0, 2.0]
[4.0, 6.0, 2.5]
[5.0, 8.0, 5.0]
[6.0, 10.0, 3.0]
[2.0, 4.0, 2.0]
[3.0, 5.0, 2.3]
[4.0, 4.0, 2.0]
[5.0, 6.0, 2.5]
[6.0, 8.0, 5.0]
[7.0, 10.0, 3.0]
[3.0, 4.0, 2.0]
[4.0, 5.0, 2.3]
[5.0, 4.0, 2.0]
[6.0, 6.0, 2.5]
[7.0, 8.0, 5.0]
[8.0, 10.0, 3.0]

Data2
<minimal.Minimal instance at 0x016A3620>
[1.0, 4.0, 2.0]
[2.0, 5.0, 2.3]
[3.0, 4.0, 2.0]
[4.0, 6.0, 2.5]
[5.0, 8.0, 5.0]
[6.0, 10.0, 3.0]
[2.0, 4.0, 2.0]
[3.0, 5.0, 2.3]
[4.0, 4.0, 2.0]
[5.0, 6.0, 2.5]
[6.0, 8.0, 5.0]
[7.0, 10.0, 3.0]
[3.0, 4.0, 2.0]
[4.0, 5.0, 2.3]
[5.0, 4.0, 2.0]
[6.0, 6.0, 2.5]
[7.0, 8.0, 5.0]
[8.0, 10.0, 3.0]

Data3
<minimal.Minimal instance at 0x016A3648>
[1.0, 4.0, 2.0]
[2.0, 5.0, 2.3]
[3.0, 4.0, 2.0]
[4.0, 6.0, 2.5]
[5.0, 8.0, 5.0]
[6.0, 10.0, 3.0]
[2.0, 4.0, 2.0]
[3.0, 5.0, 2.3]
[4.0, 4.0, 2.0]
[5.0, 6.0, 2.5]
[6.0, 8.0, 5.0]
[7.0, 10.0, 3.0]
[3.0, 4.0, 2.0]
[4.0, 5.0, 2.3]
[5.0, 4.0, 2.0]
[6.0, 6.0, 2.5]
[7.0, 8.0, 5.0]
[8.0, 10.0, 3.0]

Tool completed successfully

Upvotes: 2

Views: 657

Answers (2)

falsetru
falsetru

Reputation: 369054

Default value data is evaluated only once; data attributes of Minimal instances reference the same list.

>>> class Minimal:
...     def __init__(self, data=[]):
...         self.data = data
... 
>>> a1 = Minimal()
>>> a2 = Minimal()
>>> a1.data is a2.data
True

Replace as follow:

>>> class Minimal:
...     def __init__(self, data=None):
...         self.data = data or []
... 
>>> a1 = Minimal()
>>> a2 = Minimal()
>>> a1.data is a2.data
False

See “Least Astonishment” in Python: The Mutable Default Argument.

Upvotes: 5

Mario Rossi
Mario Rossi

Reputation: 7799

Consider the following:

def d():
   print("d() invoked")
   return 1

def f(p=d())
   pass

print"("Start")
f()
f()

It prints

d() invoked
Start

Not

Start
d() invoked
d() invoked

Why? Because default arguments are computed on function definition (and stored in some kind of internal global for reuse every subsequent time they are needed). They are not computed on each function invocation.

In other words, they behave more or less like:

_f_p_default= d()
def f(p)
   if p is None: p= _f_p_default
   pass

Make the above substitution in your code, and you will understand the problem immediately.

The correct form for your code was already provided by @falsetru . I'm just trying to explain the rationale.

Upvotes: 1

Related Questions