python can't use a property defined in the super (father) class - python

This is my code
class ElasticsearchController(object):
def __init__(self):
self.es = Elasticsearch(['blabla'], port=9200)
class MasterDataIndexController(ElasticsearchController):
def __init__(self):
self.indexName = "bbbbb"
def search(self, query):
return super.es.search(index=self.indexName, docuemntType = self.documentType, query = query)
I got this error:
AttributeError: type object 'super' has no attribute 'es'
though the super does have it.
Any idea please?

You are not initialising your super classes.
class ElasticsearchController(object):
def __init__(self):
self.es = Elasticsearch(['a.b.c.d'], port=1234)
class MasterDataIndexController(ElasticsearchController):
def __init__(self):
super(MasterDataIndexController, self).__init__()
#^^^^^^^^^^^
self.indexName = "bbbbb"
def search(self, query):
return self.es.search(index=self.indexName, docuemntType = self.documentType, query = query)
# ^^^^^ self should be fine.

You can't use super like that. super will give you access to the super class, but you should not use it as a shortcut for self. Depending if you use python or python 3, you can call super(MyClass, self) or just super(). You can use this during initialization to call the __init__ method of your superclass.
However, in most simple class hierarchies, it is not necessary to call super and your code will be clearer if you just called SuperClass.__init__(self).
After this you should be able to just use self and attribute access.

Related

How to get all class methods

I have the following classes:
class DBManagerInterface:
#abstractmethod
def __init__(self, table_name): raise NotImplementedError
#abstractmethod
def create_table(self): raise NotImplementedError
#abstractmethod
def drop_table(self): raise NotImplementedError
class DataBaseManager(DBManagerInterface):
def __init__(self, table_name):
self.table_name = table_name
def drop_table(self):
None
def create_table(self):
None
class Example:
def __init__(self, db_manager):
self.db_manager = db_manager
def test(self):
self.db_manager.create_table() # can't see the db_manager methods
In Example class I'm getting the DataBaseManager pointer.
I wan't to be able to see all the DataBaseManager methods (without the need to search them manually in DataBaseManager.py file)
I'm using python 3.5.2 and pycharm editor
Is it possible ?
PyCharm has no idea what db_manager could be, so it can't give you edit hints.
If your environment supports it, annotate the argument and PyCharm can do type inference from there:
def __init__(self, db_manager: DBManagerInterface):
self.db_manager = db_manager
or if that's not supported, you can add an annotation in a docstring:
def __init__(self, db_manager):
"""
:type db_manager: DBManagerInterface
"""
self.db_manager = db_manager
The reason you are not able to see the methods of the DataBaseManager class in the attribute db_manager in Example class, is because there is no reason why the variable db_manager in the __init__ method is supposed to be an instance of DataBaseManager.
You can either specify the type directly: https://docs.python.org/3/library/typing.html
Or you can check the instance type in the __init__ method:
class Example:
def __init__(self, db_manager):
if not isinstance(db_manager, DataBaseManager):
raise ValueError
self.db_manager = db_manager
Pycharm will afterwards understand the type of the attribute and show you all the possible methods for the object.
Try
print(dir(DataBaseManager))
From the docs:
Without arguments, return the list of names in the current local scope. With an argument, attempt to return a list of valid attributes for that object.
dir([object])

super not working with class decorators?

Lets define simple class decorator function, which creates subclass and adds 'Dec' to original class name only:
def decorate_class(klass):
new_class = type(klass.__name__ + 'Dec', (klass,), {})
return new_class
Now apply it on a simple subclass definition:
class Base(object):
def __init__(self):
print 'Base init'
#decorate_class
class MyClass(Base):
def __init__(self):
print 'MyClass init'
super(MyClass, self).__init__()
Now, if you try instantiate decorated MyClass, it will end up in an infinite loop:
c = MyClass()
# ...
# File "test.py", line 40, in __init__
# super(MyClass, self).__init__()
# RuntimeError: maximum recursion depth exceeded while calling a Python object
It seems, super can't handle this case and does not skip current class from inheritance chain.
The question, how correctly use class decorator on classes using super ?
Bonus question, how get final class from proxy-object created by super ? Ie. get object class from super(Base, self).__init__ expression, as determined parent class defining called __init__.
If you just want to change the class's .__name__ attribute, make a decorator that does that.
from __future__ import print_function
def decorate_class(klass):
klass.__name__ += 'Dec'
return klass
class Base(object):
def __init__(self):
print('Base init')
#decorate_class
class MyClass(Base):
def __init__(self):
print('MyClass init')
super(MyClass, self).__init__()
c = MyClass()
cls = c.__class__
print(cls, cls.__name__)
Python 2 output
MyClass init
Base init
<class '__main__.MyClassDec'> MyClassDec
Python 3 output
MyClass init
Base init
<class '__main__.MyClass'> MyClassDec
Note the difference in the repr of cls. (I'm not sure why you'd want to change a class's name though, it sounds like a recipe for confusion, but I guess it's ok for this simple example).
As others have said, an #decorator isn't intended to create a subclass. You can do it in Python 3 by using the arg-less form of super (i.e., super().__init__()). And you can make it work in both Python 3 and Python 2 by explicitly supplying the parent class rather than using super.
from __future__ import print_function
def decorate_class(klass):
name = klass.__name__
return type(name + 'Dec', (klass,), {})
class Base(object):
def __init__(self):
print('Base init')
#decorate_class
class MyClass(Base):
def __init__(self):
print('MyClass init')
Base.__init__(self)
c = MyClass()
cls = c.__class__
print(cls, cls.__name__)
Python 2 & 3 output
MyClass init
Base init
<class '__main__.MyClassDec'> MyClassDec
Finally, if we just call decorate_class using normal function syntax rather than as an #decorator we can use super.
from __future__ import print_function
def decorate_class(klass):
name = klass.__name__
return type(name + 'Dec', (klass,), {})
class Base(object):
def __init__(self):
print('Base init')
class MyClass(Base):
def __init__(self):
print('MyClass init')
super(MyClass, self).__init__()
MyClassDec = decorate_class(MyClass)
c = MyClassDec()
cls = c.__class__
print(cls, cls.__name__)
The output is the same as in the last version.
Since your decorator returns an entirely new class with different name, for that class MyClass object doesn't even exist. This is not the case class decorators are intended for. They are intended to add additional functionality to an existing class, not outright replacing it with some other class.
Still if you are using Python3, solution is simple -
#decorate_class
class MyClass(Base):
def __init__(self):
print 'MyClass init'
super().__init__()
Otherwise, I doubt there is any straight-forward solution, you just need to change your implementation. When you are renaming the class, you need to rewrite overwrite __init__ as well with newer name.
The problem is that your decorator creates a subclass of the original one. That means that super(Myclass) now point to... the original class itself!
I cannot even explain how the 0 arg form of super manages to do the job in Python 3, I could not find anything explicit in the reference manual. I assume it must use the class in which it is used at the time of declaration. But I cannot imagine a way to get that result in Python2.
If you want to be able to use super in the decorated class in Python 2, you should not create a derived class, but directly modify the original class in place.
For example, here is a decorator that prints a line before and after calling any method:
def decorate_class(klass):
for name, method in klass.__dict__.iteritems(): # iterate the class attributes
if isinstance(method, types.FunctionType): # identify the methods
def meth(*args, **kwargs): # define a wrapper
print "Before", name
method(*args, **kwargs)
print "After", name
setattr(klass, name, meth) # tell the class to use the wrapper
return klass
With your example it gives as expected:
>>> c = MyClass()
Before __init__
MyClass init
Base init
After __init__

How can I add to the initial definition of a python class inheriting from another class?

I'm trying to define self.data inside a class inheriting from a class
class Object():
def __init__(self):
self.data="1234"
class New_Object(Object):
# Code changing self.data here
But I ran into an issue.
class Object():
def __init__(self):
self.data="1234"
So I have the beginning class here, which is imported from elsewhere, and let's say that the class is a universal one so I can't modify the original at all.
In the original, the instance is referred to as "self" inside the class, and it is defined as self inside the definition __init__.
class New_Object(Object):
# Code changing self.data here
So if I wanted to inherit from the class Object, but define self.data inside New_Object, I thought I would have to define __init__ in New_Object, but this overrides the __init__ from New_Object
Is there any way I could do this without copypasting the __init__ from Object?
You use super to call the original implementation.
class New_Object(Object):
def __init__(self):
super(NewObject, self).__init__()
self.info = 'whatever'
That's what super is for:
class NewObject(Object):
def __init__(self):
super(NewObject, self).__init__()
# self.data exists now, and you can modify it if necessary
You can use super().__init__() to call Object.__init__() from New_Object.__init__().
What you would do:
class Object:
def __init__(self):
print("Object init")
self.data = "1234"
class New_Object(Object):
def __init__(self):
print("calling super")
super().__init__()
print("data is now", self.data)
self.data = self.data.split("3")
o = New_Object()
# calling super
# Object init
# data is now 1234
Note that you do not have to give any arguments to super(), as long as you are using Python 3.
The answer is that you call the superclass's __init__ explicitly during the subclass's __init__. This can be done either of two ways:
Object.__init__(self) # requires you to name the superclass explicitly
or
super(NewObject, self).__init__() # requires you to name the subclass explicitly
The latter also requires you to ensure that you're using "new-style" classes: in Python 3 that's always the case, but in Python 2 you must be sure to inherit from the builtin object class. In Python 3 it can actually be expressed even more simply:
super().__init__()
Personally, in most of my code the "disadvantage" of having to name the superclass explicitly is no disadvantage at all, and Object.__init__() lends transparency since it makes it absolutely clear what is being called. This is because most of my code is single-inheritance only. The super route comes into its own when you have multiple inheritance. See What does 'super' do in Python?
Python 2 example:
class Object(object):
def __init__(self):
self.data = "1234"
class NewObject:
def __init__(self):
# subclass-specific stuff
super(NewObject, self).__init__()
# more subclass-specific stuff

python 2.7 - how to call parent class constructor

I have base class like below
class FileUtil:
def __init__(self):
self.outFileDir = os.path.join(settings.MEDIA_ROOT,'processed')
if not os.path.exists(outFileDir):
os.makedirs(outFileDir)
## other methods of the class
and I am extending this class as below:
class Myfile(FileUtil):
def __init__(self, extension):
super(Myfile, self).__init__()
self.extension = 'text'
## other methods of class
But i am getting below error?
super(Myfile, self).__init__()
TypeError: super() takes at least 1 argument (0 given)
I gone through many documents and found that there are different sytex of calling super() in 2.x and 3.x. I tried both ways but getting error.
You have 2 options
old style class, you should call the super constructor directly.
class FileUtil():
def __init__(self):
pass
class Myfile(FileUtil):
def __init__(self, extension):
FileUtil.__init__(self)
new style class, inherit from object in your base class and your current call to super will be processed correctly.
class FileUtil(object):
def __init__(self):
pass
class Myfile(FileUtil):
def __init__(self, extension):
super(Myfile, self).__init__()
You may need to create the FileUtil class using the super() function as well:
class FileUtil(object):
def __init__(self):
super(FileUtil, self).__init__()
...

Python inheritance program

Error is:
Equilateral object has no attribute angle1.
please suggest how to fix this error and also please explain how self works. I am confused where to use self and where to not
class Triangle(object):
number_of_sides=3
def __init__(self,angle1,angle2,angle3):
self.angle1=angle1
self.angle2=angle2
self.angle3=angle3
def check_angles(self):
if self.angle1+self.angle2+self.angle3==180:
return True
else:
return False
class Equilateral(Triangle): //inheritance
angle=60
def __init__(self):
self.angle=self.angle1
self.angle=self.angle2
self.angle=self.angle3
man=Equilateral()
man.check_angles()
You have it the wrong way around
self.angle1= self.angle
etc
Self refers to the instantiated object, much like 'this' in java. You attach attributes to the object using this keyword.
When defining variables on an object, attributes at the beginning of your class definition do not need self- they are class attributes which all instances of the object will create on instantiation, whereas variables you change or set using self are instance variables and not found on all instances of the object.
Different from other languages, Python does not call __init__() of the super class. You have to call it yourself:
class Equilateral(Triangle):
angle=60
def __init__(self, ...):
super().__init__(...)
self.angle=self.angle1
More details
You have to call __init__ from the super class:
class Triangle(object):
number_of_sides=3
def __init__(self,angle1,angle2,angle3):
self.angle1=angle1
self.angle2=angle2
self.angle3=angle3
def check_angles(self):
return self.angle1+self.angle2+self.angle3==180:
class Equilateral(Triangle):
angle=60
def __init__(self):
Triangle.__init__(self, self.angle, self.angle, self.angle)
man=Equilateral()
man.check_angles()

Categories

Resources