Polymorphism and with statements - python

Given a class that export .csv files to a database:
import luigi
import csv
class CsvToDatabase(luigi.Task):
# (...)
def run(self):
## (...)
with open(self.input().some_attribute, 'r', encoding='utf-8') as some_dataframe:
y = csv.reader(some_dataframe, delimiter=';')
### (...) <several lines of code>
# (...)
I'm having problems trying to export a file with ISO-8859-1 encoding.
When I exclude the encoding argument from then open() function, everything works fine, but I cannot make permanent changes in the class definition (firm's other sectors uses it). So I thinked about the possibility of using polymorphism to solve it, like:
from script_of_interest import CsvToDatabase
class LatinCsvToDatabase(CsvToDatabase):
# code that uses everything in `run()` except the `some_dataframe` definition in the with statement
This possibility actually exists? How could I handle it without repeating the "several lines of code" inside the statement?

Thank you #martineau and #buran for the comments. Based on them, I will request a change in the base class definition that didn't affect other sector's work. It would look like this:
import luigi
import csv
class CsvToDatabase(luigi.Task):
# (...)
encoding_param = luigi.Parameter(default='utf-8') # as a class attribute
# (...)
def run(self):
## (...)
with open(self.input().some_attribute, 'r', encoding=self.encoding_param) as some_dataframe:
y = csv.reader(some_dataframe, delimiter=';')
### (...) <several lines of code>
# (...)
And finally, in my script, something like:
from script_of_interest import CsvToDatabase
class LatinCsvToDatabase(CsvToDatabase):
pass
LatinCsvToDatabase.encoding_param = None

You might consider as an alternative modifying the original class to add a new method get_cvs_encoding, which is used by the run method:
class CsvToDatabase(luigi.Task):
...
def get_cvs_encoding(self):
# default:
return 'utf-8'
def run(self):
## (...)
with open(self.input().some_attribute, 'r', encoding=self.get_cvs_encoding()) as some_dataframe:
y = csv.reader(some_dataframe, delimiter=';')
...
}
And then subclass this as follows:
class MyCsvToDatabase(CsvToDatabase):
def get_cvs_encoding(self):
return 'ISO-8859-1' # or None
And use an instance of the subclass. I just think this is neater and you can have multiple subclass instances "running" concurrently.

After you posted an answer, I think it's better to post some alternatives.
So, here is your current class, minimal example
class A:
def run(self):
# some code
with open('some_file.csv' , encoding='utf-8'):
pass
# more code
My idea, suggested in the comments - change class A and add parameter encoding to A.run() with default value utf-8. This way the change will not affect others (their existing code that use class A).
class A:
def run(self, encoding='utf-8'):
# some code
with open('some_file.csv' , encoding=encoding):
pass
# more code
Then in your code
a=A() # create instance of A
a.run(encoding=None)
Now, your idea to add class attribute. This is how you decided to change class A:
class A:
encoding='utf-8'
def run(self):
# some code
with open('some_file.csv', encoding=self.encoding):
pass
# more code
I don't think you need to subclass. With this new class A you can do
a=A() # create instance of the new class A
a.encoding=None # note, in run() you use self.encoding and this will work. still A.encoding is utf-8 and others will not be affected.
And if you insist to subclass the new class A
class B(A):
encoding=None
Then in your code you can use instance of class B.

Related

Finding parameters of `__init__()` or parameters needed to construct an object in python

I have scenario where I am passing a file name and checking if it has argument start as constructor if it has then I have to create instance of that class.
Consider the example where I have a file named test.py which have three class namely A,B,C now only class A has start parameter others have other different parameter or extra parameter.
#test.py
class A:
def __init__(self, start=""):
pass
class B:
def __init__(self, randomKeyword, start=""):
pass
class C:
def __init__(self):
pass
Now I want to write a script which takes test.py as an argument and create instance of A. Till now my progress is
detail = importlib.util.spec_from_file_location('test.py', '/path/to/test.py')
module = importlib.util.module_from_spec(detail)
spec.loader.exec_module(mod)
Bacially I need to write a program which finds init argument of all class in file and create an instance of file with start as init argument.
As mentioned by #deceze it's not a good idea to instantiate a class on the basis of it's init parameter as we're not sure what is there. But it's possible to do it. So I am posting this answer just so that you know how it can be done.
#test.py
class A:
def __init__(self, start=""):
pass
class B:
def __init__(self, randomKeyword, start=""):
pass
class C:
def __init__(self):
pass
One of the possibility is
#init.py
import importlib.util
from inspect import getmembers, isclass, signature
detail = importlib.util.spec_from_file_location('test.py', '/path/to/test.py')
module = importlib.util.module_from_spec(detail)
spec.loader.exec_module(module)
for name, data in getmembers(mod, isclass):
cls = getattr(mod, name)
parameter = signature(cls.__init__).parameters.keys()
# parameter start
if len(parameter) == 2 and 'start' in parameter:
object = cls(start="Whatever you want")
Ofcourse it's not the best approach so more answer are welcome and if you are in this scenario consider #deceze comment and define a builder.

How does Python support this common problem related to run time polymorphism?

I am trying to exectute the below code but I get errors.
class base:
def callme(data):
print(data)
class A(base):
def callstream(self):
B.stream(self)
def callme(data):
print("child ", data)
class B:
def stream(data):
# below statement doesn't work but I want this to run to achieve run time
# polymorphism where method call is not hardcoded to a certain class reference.
(base)data.callme("streaming data")
# below statement works but it won't call child class overridden method. I
# can use A.callme() to call child class method but then it has to be
# hardcoded to A. which kills the purpose. Any class A or B or XYZ which
# inherits base call should be able to read stream data from stream class.
# How to achive this in Python? SO any class should read the stream data as
# long as it inherits from the base class. This will give my stream class a
# generic ability to be used by any client class as long as they inherit
# base class.
#base.callme("streaming data")
def main():
ob = A()
ob.callstream()
if __name__=="__main__":
main()
I got the output you say you're looking for (in a comment rather than the question -- tsk, tsk) with the following code, based on the code in your question:
class base:
def callme(self, data):
print(data)
class A(base):
def callstream(self):
B.stream(self)
def callme(self, data):
print("child", data)
class B:
#classmethod
def stream(cls, data):
data.callme("streaming data")
def main():
ob = A()
ob.callstream()
if __name__=="__main__":
main()
Basically, I just made sure the instance methods had self parameters, and since you seem to be using B.stream() as a class method, I declared it as such.

object has no attributes. New to classes in python

import praw
import time
class getPms():
r = praw.Reddit(user_agent="Test Bot By /u/TheC4T")
r.login(username='*************', password='***************')
cache = []
inboxMessage = []
file = 'cache.txt'
def __init__(self):
cache = self.cacheRead(self, self.file)
self.bot_run(self)
self.cacheSave(self, self.file)
time.sleep(5)
return self.inboxMessage
def getPms(self):
def bot_run():
inbox = self.r.get_inbox(limit=25)
print(self.cache)
# print(r.get_friends())#this works
for message in inbox:
if message.id not in self.cache:
# print(message.id)
print(message.body)
# print(message.subject)
self.cache.append(message.id)
self.inboxMessage.append(message.body)
# else:
# print("no messages")
def cacheSave(self, file):
with open(file, 'w') as f:
for s in self.cache:
f.write(s + '\n')
def cacheRead(self, file):
with open(file, 'r') as f:
cache1 = [line.rstrip('\n') for line in f]
return cache1
# while True: #threading is needed in order to run this as a loop. Probably gonna do this in the main method though
# def getInbox(self):
# return self.inboxMessage
The exception is:
cache = self.cacheRead(self, self.file)
AttributeError: 'getPms' object has no attribute 'cacheRead'
I am new to working with classes in python and need help with what I am doing wrong with this if you need any more information I can add some. It worked when it was all functions but now that I attempted to switch it to a class it has stopped working.
Your cacheRead function (as well as bot_run and cacheSave) is indented too far, so it's defined in the body of your other function getPms. Thus it is only accessible inside of getPms. But you're trying to call it from __init__.
I'm not sure what you're trying to achieve here because getPms doesn't have anything else in it but three function definitions. As far as I can tell you should just take out the def getPms line and unindent the three functions it contains so they line up with the __init__ method.
Here are few points:
Unless you're explicitly inheriting from some specific class, you can omit parenthesis:
class A(object):, class A():, class A: are equivalent.
Your class name and class method have the same name. I'm not sure does Python confuse about this or not, but you probably do. You can name your class PMS and your method get, for example, so you'll obtain PMS.get(...)
In the present version of indentation cacheRead and cacheSave functions are simply inaccessible from init; why not move them to generic class namespace?
When calling member functions, you don't need to specify self as the first argument since you're already calling the function from this object. So instead of cache = self.cacheRead(self, self.file) you have to do it like this: cache = self.cacheRead(self.file)

I have a Python code that is given below, when I run this global name PDBAttributes is not defined

#!/usr/bin/env python2.7
##-*- mode:python;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t;python-indent:2 -*-'
import noesy
import argparse
import library
parser =argparse.ArgumentParser(description="read pdb file",
add_help=True)
parser.add_argument("file",help="protein pdb file")
library.add_standard_args( parser )
args = parser.parse_args()
def read_structure(pdbfile):
struct=[]
for line in pdbfile:
if len(line):
struct.append(PDBAttributes.read_from_line(line))
return struct
pdb=read_structure(open(args.file,'r'))
class PDBAttributes:
def __init__(self, atomindex=1, atom=noesy.Atom(), atomx=1, atomy=1, atomz=1):
self._atomindex=atomindex
self._atom=atom
self._atomx=atomx
self._atomy=atomy
self._atomz=atomz
def __str__(self):
s='ATOM %(_atomindex)d %(_atom)s at %(_atomx)8.3f %(_atomy)8.3f %(_atomz)8.3f'%self.__dict__
return s
def atom(self):
return self._atom
def atomindex(self):
return self._atomindex
def atomx(self):
return self._atomx
def atomy(self):
return self._atomy
def atomz(self):
return self._atomz
#classmethod
def read_from_line(obj,line):
tags=line.split()
atomindex=int(tags[1])
atom=noesy.Atom(tags[2],int(tags[5]))
atomx=float(tags[6])
atomy=float(tags[7])
atomz=float(tags[8])
obj=PDBAttributes(atomindex, atom, atomx, atomy, atomz)
print obj
class AtomDistance(PDBAttributes):
def distance(self, atom1,atom2):
pass
The NameError you are getting is due to the order you have placed the code in your file.
When you call read_structure to create a value for the pdb variable, it tries to look for PDBAttributes, but it has not been defined yet. If you move that line lower down in the file (below the class definition) you'll avoid that error. Note that it is OK to have the declaration of read_structure above the PDBAttributes class definition, though you might want to move it lower too to make the code easier to understand.
Here's a very simple bit of code that demonstrates the same error:
def foo():
print(foo_text)
foo() # raises a NameError
foo_text = "foo"
Here's a fixed version:
def foo():
print(foo_text)
foo_text = "foo"
foo() # no error, prints "foo"
Move your call of read_structure to follow the definition of the PDBAttributes class.
Also, in the process of reformatting your post, I see that you have mixed tabs and spaces for your indentation. Try reformatting your code to use all spaces for indentation, the recommended form is 4-space indents.
Your definition of all those getter functions looks like Java written in Python - this is a lot of extra code that is often unnecessary in Python. The recommended approach is to omit these all-they-do-is-assign-a-value-to-an-attribute-with-the-same-name-but-with-a-leading-underscore methods and just use attributes with the public names. See Python is Not Java.

Passing variables/functions between imported modules

I'm going to throw out some pseudocode. Then explain what I want, because I am not sure how to otherwise.
File_A
class Panel_A(wx.Panel)
def __init__(self):
button_a = wx.Button(parent=self)
def onButton(self, event):
pass to list view
File_B
class Panel_B(wx.panel):
def __init__(self):
listview_a = wx.ListView(parent=self)
File_C
import File_A
import File_B
panel_a = Panel_A()
panel_b = Panel_B()
OK, I have a panel in one module, that searches a database when I push button_a. The second module has a listview in it. Both modules are imported into a third module. I need to be able to pass the information from the search to listview_a in another module. I am not sure how to do this, since all the objects are declared in File_C but I need to use them in File_A.
Use the delegate design pattern:
(Pass in panel_b as an argument when instantiating Panel_A objects):
# File_A
class Panel_A(wx.Panel)
def __init__(self,panel_b):
self.panel_b=panel_b
button_a = wx.Button(parent=self)
def onButton(self, event):
pass to self.panel_b.listview_a
# File_B
class Panel_B(wx.panel):
def __init__(self):
listview_a = wx.ListView(parent=self)
# File_C
import File_A
import File_B
panel_b = Panel_B()
panel_a = Panel_A(panel_b)
You may want to pass in just the ListView, instead of the whole panel. I don't know enough about your situation to know what one would be best.
You can use a simplified version of the Observer pattern: the Panel_A class has a listener field with a fillView method that gets the list, the Panel_B implements such a method.
After the construction of both Panel_A and Panel_B, just assign to the Panel_A object's field
and call self.listener.fillView(list) from inside the onButton method

Categories