0

I am trying to subclass ConfigParser. When trying to access _sections it says

'acc' object has no attribute '_sections'

Example Code(python 2.7):

import ConfigParser
class acc(object, ConfigParser.RawConfigParser):
    def __init__(self, acc_file):
            super(acc,self).__init__()
            self.lol = 1
            print self.has_section(self.lol)

a=acc(1)
Emilgardis
  • 480
  • 4
  • 11
  • 1
    Attributes beginning with an underscore are considered private. See [the docs.](https://docs.python.org/2/reference/lexical_analysis.html#reserved-classes-of-identifiers) – Dan Dec 23 '14 at 21:53
  • Yes, however see the newest code, updated to reflect that behavior. – Emilgardis Dec 23 '14 at 21:56

2 Answers2

3

The problem has been described much better here.


This is Python's Method Resolution Order.

So, what actually happens is that the MRO "chaining" does not behave well when encoutering an old style class. Or to be specific - classes that do not call super

In your code, configParser.RawConfigParser__init__(..) is never called. To fix this you could call it manually (simply add ConfigParser.RawConfigParser.__init__(self ...) in acc's init), although I'm not sure this is recommended or even valid.

Your other option is to make all classes conform to the new style, and invoke super, or old style, and initialize explicitly.

The only thing that seems to be working is if all classic-style classes are after all new-style classes in the output of Class.mro(), and specifically after object. This will prevent super from calling them.

The other answer isn't very safe because of this scenario:

class TheClassicClass:
   def __init__(self):
       print "instantiating clasic class!"
       self._msg = "the classic class!"
   def m(self):
       print self._msg

class acc(ConfigParser.RawConfigParser, TheClassicClass, object):
    def __init__(self, acc_file):
            super(acc,self).__init__()
            self.lol = 1
            print self.has_section(self.lol)

a=acc(1)
a.m()

Fix to other answer: adding these lines to acc's __init__ should inform to explicitly instantiate the classes:

ConfigParser.RawConfigParser.__init__(self)
TheClassicClass.__init__(self)

To confirm the problem in your code, let's try to reproduce this problem in a simple setup...

We'll make old (classic) classes:

class AClassic:
    def __init__(self):
        print "init aclassic"

class BClassic:
    def __init__(self):
        print "init bclassic"
        self.name = "bclassic"

    def m(self):
        print "print from " + self.name

And new style classes that invoke super:

class ANew(object):

    def __init__(self):
        print "init anew"
        super(ANew, self).__init__()

class BNew(object):

    def __init__(self):
        print "init bnew"
        super(BNew, self).__init__()

    def m(self):
        print "print from bnew"

In the simplest case, we're manually calling __init__ to make sure classes are instantiated. This would look something like:

class TestOldStyle(AClassic, BClassic):
    def __init__(self):
        AClassic.__init__(self)
        BClassic.__init__(self)
        self.m()

print "old style"
TestOldStyle()

In this case, depth-first, left-to-right, is the order of the MRO.

Output:

old style

init aclassic

init bclassic

print from bclassic

Now let's try new style, which looks like this with super:

# will init ANew and BNew
class TestNewSuper(ANew, BNew, object):
    def __init__(self):
        super(TestNewSuper, self).__init__()

TestNewSuper()

Since both classes call super, they are both instantiated and the output is:

init anew

init bnew

Now, we try using some "hybrids" of a classic-style classes (ConfigParser.RawConfigParser is a classic-style-class, and cannot call super:

# will init ANew , but not BClassic
class TestOldStyleSuper(ANew, BClassic):
    def __init__(self):
        super(TestOldStyleSuper, self).__init__()
        self.m()

Output:

init anew

And immediately an exception:

Traceback (most recent call last):
  File "./inhert.py", line 35, in <module>
    TestOldStyleSuper()
  File "./inhert.py", line 33, in __init__
    self.m()
  File "./inhert.py", line 15, in m
    print "print from " + self.name
AttributeError: 'TestOldStyleSuper' object has no attribute 'name'

This happens since BClassic is not instantiated.

More examples of unexpected behaviour:

# init ANew, BClassic
class TestHybrid(ANew, BClassic, BNew):
    def __init__(self):
        super(TestHybrid, self).__init__()
        self.m()

TestHybrid()

will initialize ANew and BClassic, but not BNew:

init anew

init bclassic

print from bclassic

And creating an incosistent MRO:

# no consistent MRO exception
class TestHybrid(ANew, object, BNew):
    def __init__(self):
        super(TestHybrid, self).__init__()
        self.m()

TestHybrid()

Exception:

Traceback (most recent call last):
  File "./inhert.py", line 33, in <module>
    class TestHybrid(ANew, object, BNew):
TypeError: Error when calling the metaclass bases
    Cannot create a consistent method resolution
order (MRO) for bases BNew, object
Community
  • 1
  • 1
Reut Sharabani
  • 30,449
  • 6
  • 70
  • 88
2

Reverse the order of subclassing as follows:

import ConfigParser

class acc(ConfigParser.RawConfigParser, object):
    def __init__(self, acc_file):
        super(acc, self).__init__()
        self.lol = 1
        print self.has_section(self.lol)

a=acc(1)
kartikg3
  • 2,590
  • 1
  • 16
  • 23