Python 2.7 multiple inheritance - python

Why this simple code doesn't work for Python 2.7 ? Please, help. Most likely I misuse super method in 'New Style' for classes.
class Mechanism(object):
def __init__(self):
print('Init Mechanism')
self.__mechanism = 'this is mechanism'
def get_mechanism(self):
return self.__mechanism
class Vehicle(object):
def __init__(self):
print('Init Vehicle')
self.__vehicle = 'this is vehicle'
def get_vehicle(self):
return self.__vehicle
class Car(Mechanism, Vehicle):
def __init__(self):
super(Car, self).__init__()
c = Car()
print(c.get_mechanism())
print(c.get_vehicle())
The error:
Init Vehicle
Traceback (most recent call last):
File "check_inheritance.py", line 22, in <module>
print(c.get_mechanism())
File "check_inheritance.py", line 7, in get_mechanism
return self.__mechanism
AttributeError: 'Car' object has no attribute '_Mechanism__mechanism'
EDIT
Fixed def __init(self): in Mechanism class onto def __init__(self):
The correct answer is to use super method in all classes. Not only in Car class. See the answer of Martijn Pieters
Try to avoid double underscore __ for private variables. It is not a Python way (style of code). See the discussion for more info here.

You have 2 issues:
You misnamed the __init__ method of Mechanism; you are missing two underscores.
Your __init__ methods do not cooperate correctly in a multiple inheritance situation. Make sure you always call super(...).__init__(), in all your __init__ methods.
The following code works:
class Mechanism(object):
def __init__(self):
super(Mechanism, self).__init__()
print('Init Mechanism')
self.__mechanism = 'this is mechanism'
def get_mechanism(self):
return self.__mechanism
class Vehicle(object):
def __init__(self):
super(Vehicle, self).__init__()
print('Init Vehicle')
self.__vehicle = 'this is vehicle'
def get_vehicle(self):
return self.__vehicle
class Car(Mechanism, Vehicle):
def __init__(self):
super(Car, self).__init__()
Demo:
>>> c = Car()
Init Vehicle
Init Mechanism
>>> print(c.get_mechanism())
this is mechanism
>>> print(c.get_vehicle())
this is vehicle
You should probably also not use double-underscore names. See Inheritance of private and protected methods in Python for the details, but the short reason is that you do not have a use case here for class-private names, as you are not building a framework meant to be extended by third parties; that's the only real usecase for such names.
Stick to single-underscore names instead, so _mechanism and _vehicle.

Related

Initializing an attribute in a child class that is used in the parent class

I am using a 3rd party Python library (wxPython), which has a buggy class in one of its modules.
The problematic code section looks like this:
def OnText(self, event):
value = self.GetValue()
if value != self.__oldvalue:
pass # Here some more code follows ...
self.__oldvalue = value
The problem is the if statement, because at the first call to this method self.__oldvalue has not been initialized yet. So for a workaround until this bug has been fixed by the library devs I thought I could fix this with a little workaround. I simply wanted to derive a child class from that faulty class and initialize self.__oldvalue in this constructor:
class MyIntCtrl(wx.lib.intctrl.IntCtrl):
def __init__(self, *args, **kw):
self.__oldvalue = None
super().__init__(*args, **kw)
However, now when I use this new class MyIntCtrl instead of the original IntCtrl class, I do get exactly the same error as before:
Traceback (most recent call last):
File "/usr/local/lib/python3.6/dist-packages/wx/lib/intctrl.py", line 509, in OnText
if value != self.__oldvalue:
AttributeError: 'MyIntCtrl' object has no attribute '_IntCtrl__oldvalue'
Now I am wondering: What am I doing wrong, how else can I fix this issue in a child class?
Any member of class which starts with __ (double underscore) is private, you can use single underscore _ or not use underscores in naming for access them in derived classes.
class Parent:
def __init__(self):
self.__private_field = "private field"
self._protected_field = "protected field"
self.public_field = "public field"
class Child(Parent):
def __init__(self):
pass
def do(self):
print(self.__private_field) # It will throw exception
print(self._protected_field) # It will not throw exception
print(self.public_field) # It will not throw exception
Or you can bypass private/protected members by calling them like:
print(_Parent__private_field)

super() in a decorated subclass in Python 2

I'm trying to use super in a subclass which is wrapped in another class using a class decorator:
def class_decorator(cls):
class WrapperClass(object):
def make_instance(self):
return cls()
return WrapperClass
class MyClass(object):
def say(self, x):
print(x)
#class_decorator
class MySubclass(MyClass):
def say(self, x):
super(MySubclass, self).say(x.upper())
However, the call to super fails:
>>> MySubclass().make_instance().say('hello')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in say
TypeError: super(type, obj): obj must be an instance or subtype of type
The problem is that, when say is called, MySubclass doesn't refer to the original class anymore, but to the return value of the decorator.
One possible solution would be to store the value of MySubclass before decorating it:
class MySubclass(MyClass):
def say(self, x):
super(_MySubclass, self).say(x.upper())
_MySubclass = MySubclass
MySubclass = class_decorator(MySubclass)
This works, but isn't intuitive and would need to be repeated for each decorated subclass. I'm looking for a way that doesn't need additional boilerplate for each decorated subclass -- adding more code in one place (say, the decorator) would be OK.
Update: In Python 3 this isn't a problem, since you can use __class__ (or the super variant without arguments), so the following works:
#class_decorator
class MySubclass(MyClass):
def say(self, x):
super().say(x.upper())
Unfortunately, I'm stuck with Python 2.7 for this project.
The problem is that your decorator returns a different class than python (or anyone who uses your code) expects. super not working is just one of the many unfortunate consequences:
>>> isinstance(MySubclass().make_instance(), MySubclass)
False
>>> issubclass(MySubclass, MyClass)
False
>>> pickle.dumps(MySubclass().make_instance())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <class '__main__.MySubclass'>: it's not the same object as __main__.MySubclass
This is why a class decorator should modify the class instead of returning a different one. The correct implementation would look like this:
def class_decorator(wrapped_cls):
#classmethod
def make_instance(cls):
return cls()
wrapped_cls.make_instance = make_instance
return wrapped_cls
Now super and everything else will work as expected:
>>> MySubclass().make_instance().say('hello')
HELLO
The problem occurs because at the time when MySubclass.say() is called, the global symbol MySubclass no longer refers to what's defined in your code as 'class MySubclass'. It is an instance of WrapperClass, which isn't in any way related to MySubclass.
If you are using Python3, you can get around this by NOT passing any arguments to 'super', like this:
super().say(x.upper())
I don't really know why you use the specific construct that you have, but it does look strange that a sub-class of MyClass that defines 'say()' - and has itself a 'say()' method in the source code would have to end up as something that does not have that method - which is the case in your code.
Note you could change the class WrapperClass line to make it read
class WrapperClass(cls):
this will make your wrapper a sub-class of the one you just decorated. This doesn't help with your super(SubClass, self) call - you still need to remove the args (which is OK only on Python3), but at least an instance created as x=MySubclass() would have a 'say' method, as one would expect at first glance.
EDIT: I've come up with a way around this, but it really looks odd and has the disadvantage of making the 'wrapped' class know that it is being wrapped (and it becomes reliant on that, making it unusable if you remove the decorator):
def class_decorator(cls):
class WrapperClass(object):
def make_instance(self):
i = cls()
i._wrapped = cls
return i
return WrapperClass
class MyClass(object):
def say(self, x):
print(x)
#class_decorator
class MySubclass(MyClass):
def say(self, x):
super(self._wrapped, self).say(x.upper())
# make_instance returns inst of the original class, non-decorated i = MySubclass().make_instance() i.say('hello')
In essence, _wrapped saves a class reference as it was at declaration time, consistent with using the regular super(this_class_name, self) builtin call.

python name mangling in __init__ seems inconsistent

I'm trying to use a subclass that enhances instead of overriding the base class. I'm using the super method to call the base class. I find that I need to use the name mangling feature in __init__ (but only in init?) to make the code work. So for the heck of it I made the this print example. Since I didn't use name mangling I expected it to call subclass twice when I did the init, instead it calls the base class
It seems that __init__ sometimes sees the base class and sometimes sees the subclass. I'm sure it's just an incomplete understanding on my part, but was do I need name mangling for the real code, when in the print example it calls the base and subclass just fine?
the code
class base:
def __init__(self):
self.print()
def print(self):
print("base")
class subclass(base):
def __init__(self):
super(subclass, self).__init__()
self.print()
def print(self):
super(subclass, self).print()
print("subclass")
x = base()
x.print()
print("--")
y = subclass()
y.print()
the output - why doesn't y = subclass() print subclass instead of base since I didn't use name mangling?
> ./y.py
base
base
--
base
subclass
base
subclass
base
subclass
broken code when I don't use name mangling, works when I use self.__set and __set = set (the commented code). It gets the following error when I don't use __set:
File "./x.py", line 5, in __init__
self.set(arg)
TypeError: set() missing 1 required positional argument: 'arg2'
the code:
class base:
def __init__(self, arg):
self.set(arg)
# self.__set(arg)
# __set = set
def set(self, arg):
self.arg = arg
def print(self):
print("base",self.arg)
class subclass(base):
def __init__(self, arg1, arg2):
super(subclass, self).__init__(arg1)
self.set(arg1, arg2)
def set(self, arg1, arg2):
super(subclass, self).set(arg1)
self.arg2 = arg2
def print(self):
super(subclass, self).print()
print("subclass", self.arg2, self.arg)
x = base(1)
x.print()
x.set(11)
x.print()
y = subclass(2,3)
y.print()
y.set(4,5)
y.print()
======= update =======
I rewrote the code to look like this:
class base:
def __init__(self):
print("base init")
self.print()
def print(self):
print("base print")
class subclass(base):
def __init__(self):
print("sc init")
super(subclass, self).__init__()
print("sc after super")
self.print()
def print(self):
print("subclass print start")
super(subclass, self).print()
print("subclass print")
y = subclass()
print("--")
y.print()
when I run I get this output:
sc init
base init
subclass print start <<<< why is the subclass print called here
base print
subclass print
sc after super
subclass print start
base print
subclass print
--
subclass print start
base print
subclass print
why does the self.print in the base init call the subclass print when I'm initing the subclass? I was expecting that to call the base print. it does call the base print when I call it outside of the init.
Your subclass print explicitly calls the superclass one. So every time subclass.print is called, both "base" and "subclass" will be printed. This happens three times, because you call the print method three times: in subclass.__init__, in base.__init__ (which is called by subclass.__init__), and in subclass.print (which calls the superclass version).
In your "set" example, subclass.__init__ calls base.__init__, which tries to call self.set with just one argument. But since you are instantiating subclass, self.set is subclass.set, which takes two arguments.
It's unclear what you're trying to achieve with these examples. Your subclass doesn't really need to call base.__init__, because all that would do is call base.set, and you're already calling that from subclass.set. So even if you succeeded with all your calls, it would result in some methods getting called multiple times, just like with the print example.
My impression is that you're getting a bit carried away and trying to have every method call its superclass version. That's not always a good idea. If you write a subclass, and it calls a superclass method, you need to make sure that the subclass still provides an interface that's compatible with what the superclass expects. If it doesn't, you may need to not call the superclass method and instead have the subclass incorporate its functionality "inline" (although this may be more risky if other classes out in the world have made assumptions about how the base class works). The upshot is that you always need to think about what methods call which others; you can't just call every superclass method everywhere and expect that to work.

Construct object via __init__ and ignore constructor exception

I have a Python class whose __init__ method raises a custom exception called WrongFileSpecified.
However, when I write a unit test, I want to assign the attributes of the instance object from a test fixture. So normally what I would be doing is reading data off a file and then working with the instance object.
But with the test, I cannot use any test files, so I basically need to hard code the data in the instance object in the setUp method of the unit test. Is there any way to get a instance created without __init__ complaining about the exception?
Sample code:
class A(object):
def __init__(self, folderPath):
#check folder path using os.isdir() otherwise raise exception
#...
self.folderPath = folderPath
#Call load record
self._load_records() #uses self.folderPath and raises exceptions as well
#Note i cannot avoid raising these exceptions, its required
class TestA(unittest.TestCase):
.......
obj = None
def setUp(self):
obj = A('fake folder path')
obj.val1 = "testparam1"
obj.param2 = "testparam2"
def test_1(self):
.....
You can create an empty object, bypassing __init__ by using __new__.
obj = obj_type.__new__(obj_type)
Note that obj_type is the appropriate type object. This is a little hacky but it works. You are reponsible for setting the object's members.
Edit: here is an example.
class Foo():
def __init__(self):
self.x = 1
self.y = 2
def say_hello(self):
print('Hello!')
r = Foo.__new__(Foo)
r.say_hello()
print(r.x)
Console output:
Hello!
Traceback (most recent call last):
File "C:\WinPython-64bit-3.3.5.7\python-
3.3.5.amd64\Scripts\projects\luc_utils\dev\test\
unit_test_serialization.py", line 29, in <module>
print(r.x)
AttributeError: 'Foo' object has no attribute 'x'
Here are two options:
Refactor the file loading out to a class method, which is the Pythonic method of providing an alternate constructor (see below); or
Provide an additional parameter to __init__ to suppress the exceptions when necessary (e.g. def __init__(self, folderPath, suppress=False), or validate=True, whichever makes more sense for your usage).
The latter is a bit awkward, in my opinion, but would mean that you don't have to refactor existing code creating A instances. The former would look like:
class A(object):
def __init__(self, ...):
"""Pass whatever is loaded from the file to __init__."""
...
#classmethod
def from_file(cls, folderPath):
"""Load the data from the file, or raise an exception."""
...
and you would replace e.g. a = A(whatever) with a = A.from_file(whatever).
There is a very useful module called mock, you can check it out later, I feel that in this case it will be too much. Instead, you should consider redesigning your class, like this, for example:
class A(object):
def __init__(self, folderPath):
self.folderPath = folderPath
def _load_records(self)
#check folder path using os.isdir() otherwise raise exception
...
#uses self.folderPath and raises exceptions as well
...
#classmethod
def load_records(cls, folderpath):
obj = cls(folderpath)
obj._load_records()
return obj
# Usage
records = A.load_records('/path/to/records')
Then you can do:
class TestA(unittest.TestCase):
.......
obj = None
def setUp(self):
self.obj = A('fake folder path')
self.obj.val1 = "testparam1"
self.obj.param2 = "testparam2"
def test_1(self):
self.assertRaises(self.obj._load_records, HorribleFailureError)
Also i highly recommend to check out pytest, it has wonderful facilities for test fixtures, including fixtures for files and folders.

python inheritance and/or descriptors differences in 2.7 and 3

I'm trying to understand a little more about how python classes and descriptors work. I have the following code.
class Base():
def __init__(self):
self.a = 'base_a'
def get_a(self):
return self._x
def set_a(self,val):
self._x = val
def delete_a(self):
pass
a = property(get_a,set_a,delete_a)
class Derived(Base):
def __init__(self):
Base.__init__(self)
#property
def a(self):
return 'derived_a'
t = Derived()
print(t.a)
Running in Python 2.7, I get
[carl#home tmp-carl]$ python2.7 test.py
base_a
Running in Python 3.3, I get
[carl#home tmp-carl]$ python3 test.py
Traceback (most recent call last):
File "test.py", line 25, in <module>
t = Derived()
File "test.py", line 18, in __init__
Base.__init__(self)
File "test.py", line 5, in __init__
self.a = 'base_a'
AttributeError: can't set attribute
I think I understand the Python3.3 behavior. t is an instance of Derived so Base::__init__ searches for t.__dict__['a'], doesn't find it. It goes to Derived.__dict__['a'], finds the read-only property and errors. It never makes it to Base.__dict__['a'], which is read-write.
But if this is true, I don't understand why Python2.7 works at all. It seems as if it completely ignores the overwritten property in Derived.
Can anyone explain this to me?
Python 2.x has two types of classes: old-style, and new-style. Descriptors (of which property is one) only work in new-style classes. In order to create a new-style class in 2.x you must derive from object.
class Base(object):
All classes in 3.x are new-style classes.

Categories

Resources