2

I have a file as such:

1 23
2 21
5 23
561 2
73 19781

And with this function:

def readx(x):
    return {int(line.split()[0]):int(line.split()[1]) for line in x.split('\n')}

I can get this:

{1: 23, 2: 21, 5: 23, 73: 19781, 561: 2}

But I need to put it into some sort of class object, so I tried this:

z = """1 23
2 21
5 23
561 2
73 19781"""

def readx(x):
    return {int(line.split()[0]):int(line.split()[1]) for line in x.split('\n')}


class Foo(dict):
    def __init__(self, x):
        self = readx(x)

f = Foo(z)
print f

But it returns a None instead of the dictionary.

  1. Is there a more pythonic way to do readx()? It a little ugly as it is now.
  2. How do i get the class object to work and make foo a dict with keys and values?
alvas
  • 115,346
  • 109
  • 446
  • 738
  • Actually this `{1: 23, 2: 21, 5: 23, 73: 19781, 561: 2}` is already "some sort of class object", isn't it? It's an instance of `dict` – Dominik Neise Sep 26 '14 at 08:20
  • @alvas try to do `for k,v in readx(x).items(): self[k] = v`... – Saullo G. P. Castro Sep 26 '14 at 08:20
  • in your constructor use self.__x = readx(x) and a property to your class to access the variable self.__x – tschm Sep 26 '14 at 08:21
  • @DominikNeise, it is but it doesn't solve the problem and I want to build a class object from it. let's say i want to call a function to return a function in `__init__()` (it is more complicated than readx()). – alvas Sep 26 '14 at 08:22

3 Answers3

3

Use super:

class Foo(dict):
    def __init__(self, x):
        super(Foo, self).__init__(readx(x))

This will call the dict constructor on the data, copying it into the object.

Note that this works on Python 3 as well.

Community
  • 1
  • 1
Lambda Fairy
  • 13,814
  • 7
  • 42
  • 68
  • 1
    is there a reason why super() is necessary? – alvas Sep 26 '14 at 08:29
  • It's not necessary in this case; you can use `dict.__init__(self, readx(x))` instead if you want to. But since there are other cases where it *does* matter, we might as well make a habit of using `super` now. – Lambda Fairy Sep 26 '14 at 08:49
2

An alternative without super is:

class Foo(dict):
    def __init__(self, x):
        for k, v in readx(x).items():
            self[k] = v
Saullo G. P. Castro
  • 56,802
  • 26
  • 179
  • 234
2

To the first question, yes, this comprehension is a bit unreadable, a simple loop might work better:

def read_dict(fp):
   d = {}
   for line in fp:
        k, v = line.strip().split();
        d[int(k)] = int(v)
   return d

where fp is any iterable, e.g. a file object.

To the second question, if your __init__ doesn't do any useful job, just get rid of it, and pass the result of read as an argument to the constructor, which will then transparently invoke the parent dict.__init__:

class Foo(dict):
    # stuff

with open(path) as fp:
   foo = Foo(read_dict(fp))

Alternatively, instantiate Foo right in read and return it:

 def read_foo(fp):
   d = Foo()
   for line in fp:
       # etc, just as above

and then simply

with open(path) as fp:
   foo = read_foo(fp)

You can also make read a class method in Foo to structure your code better:

class Foo(dict):

   @classmethod
   def read(cls, fp):
       d = cls()
       for line in fp:
          #etc

and then:

with open(path) as fp:
   foo = Foo.read(fp)

Finally, if you want to initialize Foo directly from the fp object, you do need __init__:

class Foo(dict):
    def __init__(self, fp):
        for line in fp:
            k, v = line.strip().split()
            self[int(k)] = int(v)

and then

x = Foo(fp)

This code should be used with care though, since it violates the Liskov substitution principle, and that is generally not a good thing.

georg
  • 211,518
  • 52
  • 313
  • 390
  • 1
    +1 for the class method. By building the structure inline, we eliminate the overhead of an extra copy. – Lambda Fairy Sep 26 '14 at 08:54
  • but the `Foo` will be `type` and not a Foo object right? So if i want to put more methods into Foo and `foo` needs to access it, would it still be possible? or is it like putting everything outside as normal `def` functions? – alvas Sep 26 '14 at 09:17
  • @alvas: no, if you do `x=Foo(whatever)`, `x` will be `type Foo`, no matter if you have `__init__` or not. – georg Sep 26 '14 at 09:20
  • ooooo. So when i do `foo = Foo.read(fp)`, foo is of a Foo object? – alvas Sep 26 '14 at 09:21
  • http://stackoverflow.com/questions/12179271/python-classmethod-and-staticmethod-for-beginner – alvas Sep 26 '14 at 09:22
  • 1
    @alvas: yes, because `Foo.read` does `d=cls()`, which is basically the same as `d=Foo()` (first argument to a class method is the class itself). – georg Sep 26 '14 at 09:23
  • Is there a way such that I use `foo = Foo(fp)` instead `foo = Foo.read(fp)`? – alvas Sep 26 '14 at 10:38
  • Once again, thanks a million and noted on the Liskov! – alvas Sep 26 '14 at 15:16