I have two classes within separate moduls, one class containing actual functionality and the other containing test functionality.
ModuleA:
def __init__(self, path):
self.import_path = None
def import_data(self):
self.import_path = sales.__path__["data"]
self.input = pd.read_excel(
self.import_path,
index_col=False,
engine="openpyxl",
dtype=object,
)
return self.input
ModuleB:
def __init__(self, path):
self.import_test_path = None
def test_import_data(self):
self.import_test_path = self.__path__["data"]
# Replace actual data paths with test data paths
ModuleA.import_path = self.import_test_path
ModuleA.import_data()
The problem is that while I have defined a test path in ModuleB, I still cannot overwrite the import_path instantiated in ModuleA even if I call the method, because the path is defined within the method. Is there a way to replace this path with the path from ModuleA, e.g. via Monkeypatching? Thanks!
Answer found. I just had to place the hardcoded path outside the ModuleA method itself and into its init method. Then, when only calling the method but not instantiating the class, I could pass the right path:
ModuleA:
def __init__(self, path):
self.import_path = sales.__path__["data"]
def import_data(self):
self.input = pd.read_excel(
self.import_path,
index_col=False,
engine="openpyxl",
dtype=object,
)
return self.input
Related
I'm fairly new to Python and I've been struggling with inheritance concepts on a work project. The base class would get directory paths, table names etc from a config file (I'm keeping mosthis out because it's irrelevant to the question below) and pass this to a Child class via super()__ init __.
The base class also has a method to export a dataframe to MS SQL Server. The problem is, I can't pass the pandas dataframe from my Child class to my Parent Class, only hardcoded values. Reason I'm doing this is because the dataframes are fairly different from one another (demanding unique data manipulation), but they all come from the same place and will be placed in the same database.
So pretty much I need to run SQL_Export from Parent with the dataframe defined in the Child as input. I tried placing SQLExport in my init, but I get
AttributeError: 'function' object has no attribute 'to_sql'
What I have:
from configparser import ConfigParser
import pandas as pd
config_object = ConfigParser()
config_object.read('config.ini')
class Parent:
def __init__(self, report, df, date=today):
self.report = report
self.df = df
self.name = config_object[self.report]['name']
self.table_name = config_object[self.report]['table_name']
def SQLExport(self):
return self.df.to_sql(self.table_name, con="engine")
class Child(Parent):
def __init__(self):
super().__init__('REPORTNAME', self.load_dataframe)
super().SQLexport(self)
def load_dataframe(self):
self.df = pd.read_json(self.name + ".json")
if __name__ == '__main__':
x = Child()
Thanks!!
There are multiple issues with the code
super().SQLexport(self) function name is wrong in your client class(it should be self.SQLExport) and it wont accept any parameters so self is not required to pass
pd.read_json(self.name. +".json") - Child class has no attribute name, so your self.name will fail
self.load_dataframe function is being passed to super.init method instead of dataframe and no return statement(return df) in load_dataframe function in child class
the above issues are corrected and, you can try with below code which write the dataframe in sqlite in memory database
from configparser import ConfigParser
import pandas as pd
from sqlalchemy import create_engine
engine = create_engine('sqlite:///:memory:')
config_object = ConfigParser()
config_object.read('config.ini')
class Parent:
def __init__(self, report, df):
self.report = report
self.df = df
self.name = config_object[self.report]['name']
self.table_name = config_object[self.report]['table_name']
def SQLExport(self):
print(self.table_name)
return self.df.to_sql(self.table_name, con=engine)
class Child(Parent):
def __init__(self):
self.name="test_123"
super().__init__('REPORTNAME', self.load_dataframe())
super().SQLExport()
def load_dataframe(self):
self.df = pd.read_json(self.name + ".json")
return self.df
def read_data(self):
print(engine.execute("SELECT * FROM sample_table").fetchall())
if __name__ == '__main__':
x = Child()
x.read_data()
Declare global variable and passing value across methods
in Python OOP.
So, I would like to pass the result of nethod1 to
method2.
For example
import pandas as pd
class ExampleClass(object):
def method1(self):
file_path="/some/path/"
file_data="populaion_data.csv"
data=pd.read_csv(file_path+file_data)
res = data.head(5)
def method2(self):
"""
In his method, i would like to do the following tasks
(1)read the "res" from the method1.
(2)want to get the value of "file_path" from method1 again.
"""
And, also I think it would be better to declare the
"file_path" value as a global vaiable, so I can use that
value across several methods.
If you want the variable to be accessible across methods in the same class, you can just declare an instance variable:
import pandas as pd
class ExampleClass(object):
def __init__(self):
self.file_path = "/some/path/"
self.res = ''
def method1(self):
file_data="populaion_data.csv"
data=pd.read_csv(self.file_path+file_data)
self.res = data.head(5)
def method2(self):
"""
In his method, i would like to do the following tasks
(1)read the "res" from the method1.
(2)want to get the value of "file_path" from method1 again.
"""
#self.file_path and self.res will be accessible here too.
print (self.file_path, self.res)
a = ExampleClass()
a.method2()
You can do the following:
def method1(self):
self.file_path = "some/path/"
file_data = "population_data.csv"
data=pd.read_csv(file_path+file_data)
self.res = data.head(5)
def method2(self):
print(self.file_path, self.res)
Note that you will have to call method2 after method1 however. You can also call method1 from method2 and then use the values like this:
def method1(self):
file_path = "some/path/"
file_data = "population_data.csv"
data=pd.read_csv(file_path+file_data)
res = data.head(5)
return (file_path, res)
def method2(self):
file_path, res = self.method1()
You can also define it as a class variable or a static variable.
To define it as a static variable simply do:
class ExampleClass:
file_path = "some/path"
data=pd.read_csv(file_path+file_data)
res = data.head(5)
def method2(self):
print(self.file_path, self.res)
This is not an OOP question. With OOP you manipulate objects defined by Classes which are, from a basic point of view, types A type is a component having a state (data) and behavior (integrated functions called methods). Thus, there is no global variables in OOP. Data can be provided to objects trough methods parameters and the result of the processing should be returned by the method.
In this cas, I don't see the reason why those functions are within a class except if you need objects as defined in previous answers. Based on your code example, this is resolved with functions for which, data should be passed as parameters:
import pandas as pd
DEFAULT_FILE_PATH ="/some/path/"
def method1(filename, file_path='/'):
data=pd.read_csv(file_path + file_data)
return data.head(5)
def method2(data, file_path='/'):
"""
In his method, i would like to do the following tasks
(1)read the "res" from the method1.
(2)want to get the value of "file_path" from method1 again.
"""
So you know what file_path is, you can use it as
method2(method1("populaion_data.csv", DEFAULT_FILE_PATH), DEFAULT_FILE_PATH)
I'm trying to "simulate" namespacing in python. I'm using inner and outer class hirarchies to create my namespaces. For example you want to save paths of files (like resources) in one location. I tried something like this:
src = #path to source folder
class Resources:
root = src + "Resources\\"
class Fonts:
root = Resources.root + "fonts\\"
font1 = root + "font1.ttf"
font2 = root + "font2.ttf"
class Images:
root = Resources.root + "images\\"
logo = root + "logo"
image1= root + "image1"
class StyleSheets:
root = Resources.root + "stylesheets\\"
default = root + "default.qss"
class JsonData:
root = src + "Data\\"
class TableEntries:
root = JsonData.root
entries1 = root + "Entries1.json"
entries2 = root + "Entries2.json"
Accessing elements would look like this:
logoPath = Resources.Images.image1
Unfortunatly this isn't working due to the following error:
root = Resources.root + "fonts\\"
NameError: name 'Resources' is not defined
My Question
Is it possible to set class variables of inner class based on class variables of outer class? If not, is there another way to access the elements as shown above without using multiple files?
Is it possible to set class variables of inner class based on class variables of outer class?
Not without ressorting to a custom metaclass to process the inner classes, which will certainly not help readability nor maintainability (and will be - rightly - seen by any experienced python programmer as a total WTF).
EDIT : well actually for your example snippet the metaclass solution is not that complicated, cf the end of this answer
The reason is that in Python almost everything happens at runtime. class is an executable statement, and the class object is only created and bound to it's name after the end of the whole class statement's body.
If not, is there another way to access the elements as shown above without using multiple files?
Quite simply (dumbed down example):
import os
# use a single leading underscore to mark those classes
# as "private" (=> not part of the module's API)
class _Fonts(object):
def __init__(self, resource):
self.font1 = os.path.join(resource.root, "font1.ttf")
self.font2 = os.path.join(resource.root, "font2.ttf")
class _Resources(object):
def __init__(self, src):
self.root = os.path.join(rsc, "Ressources")
self.Fonts = _Fonts(self)
# then instanciate it like any other class
src = "/path/to/source/folder"
Resources = _Resources(src)
print(Resources.Fonts.font1)
EDIT : after a bit more thinking a metaclass-based solution for your use case would not be that complicated (but this will NOT be anything generic):
import os
class ResourcesMeta(type):
def __init__(cls, name, bases, attrs):
for name in attrs:
obj = getattr(cls, name)
if isinstance(obj, type) and issubclass(obj, SubResource):
instance = obj(cls)
setattr(cls, name, instance)
class SubResourceMeta(type):
def __new__(meta, name, bases, attrs):
if not bases:
# handle the case of the SubResource base class
return type.__new__(meta, name, bases, attrs)
root = attrs.pop("root")
cls = type.__new__(meta, name, bases, {})
cls._root = root
cls._attrs = attrs
return cls
class SubResource(metaclass=SubResourceMeta):
def __init__(self, parent):
self.root = os.path.join(parent.root, self._root)
for name, value in self._attrs.items():
setattr(self, name, os.path.join(self.root, value))
class Resources(metaclass=ResourcesMeta):
root = "/path/to/somewhere"
class Fonts(SubResource):
root = "fonts"
font1 = "font1.ttf"
font2 = "font2.ttf"
class Images(SubResource):
root = "images"
logo = "logo"
image1= "image1"
I think that you do not have clear the concept of class and instaces in OOP. If you want to store this kind of information Resources shoult not be a class, it should be an instance of a Dirclass.
class Dir:
def __init__(self, path="/", parent=None):
self.parent = parent
self.path = path
self.contents = {}
def __getitem__(self, key):
return self.contents[key]
def create_subdir(name):
self.contents[name] = Dir(os.path.join(self.path + name), self)
def add_file(file):
self.contents[file] = file # You should probably also have a File type
# ...
resources = Dir(os.path.join(src, "Resources"))
resources.create_subdir("fonts")
fonts = resources["fonts"]
fonts.add_file("font1.ttf")
...
I've used os.path.join function to delegate to Python choosing the correct delimiter for each SO instead of hardcoding Windows delimiters as you have. The __getitem__method allows to get items as if the variable was a dictionary directly.
EDIT:
You could take advantage of pathlib standard module and add the attribute access notation (using '.' to acces the subdirectories) if you don't like the div operator usage of pathlib.
from pathlib import Path as Path_, WindowsPath as WPath_, PosixPath as PPath_
import os
class Path(Path_):
def __new__(cls, *args, **kwargs):
return super().__new__(WindowsPath if os.name == 'nt' else PosixPath,
*args, **kwargs)
def __getattr__(self, item):
if item == '_str':
raise AttributeError
for i in self.iterdir():
if i.name == item:
return i
raise AttributeError
class WindowsPath(WPath_, Path):
pass
class PosixPath(PPath_, Path):
pass
current = Path()
subdir = current.subdir_name # current / 'subdir_name'
I would like a class that in its initialize checks if filename exists. If it does it should initialize itself with filename, otherwise it should run init. At a later point I can then run a save method, saving the entire object.
A sketch of what I want:
class data(object):
def __init__(self, filename):
if does_not_exist(filename): # create new
[... expensive computations]
self.save(filename)
else: # load existing
with open(filename,'rb') as fp:
self = pickle.load(fp)
def save(self, filename):
with open(filename,'wb') as fp:
pickle.dump(self, fp)
When loading I know that I can do something like
tmp = pickle.load(fp)
self.a = tmp.a
self.b = tmb.b
...
But I hope that there is a better way
I assume this question has been asked before, but couldn't find it :/
Assigning to self within __init__ is meaningless, since you're not modifying the object that self points to -- you're just binding the variable name self in the function to a different object.
What you can do instead is use a staticmethod or classmethod to perform the optional loading from cache:
class Data(object):
#classmethod
def init_cached(cls, filename):
if not os.path.exists(filename): # create new
result = cls(filename)
result.save(filename)
return result
else:
with open(filename, 'rb') as fp:
return pickle.load(fp)
def __init__(self, filename):
pass # [... expensive computations]
Now, use Data.init_cached() instead of Data() to initialize your object.
A more fancy approach would involve overriding Data.__new__() to achieve the same thing, but where initialization with Data() transparently checks if a cached version exists:
class Data(object):
def __new__(cls, filename):
if not os.path.exists(filename): # create new
return super(Data, cls).__new__(cls, filename, _save=True)
else:
with open(filename, 'rb') as fp:
return pickle.load(fp)
def __init__(self, filename, _save=False):
# [... expensive computations]
if _save:
self.save(filename)
Further reading: Python's use of __new__ and __init__?
I would like to create unit test for filename_to_txt method:
class some_panel(wx.Panel):
def __init__(self,parent,Some_class,some_handler,Some_Event):
wx.Panel.__init__(self,parent=parent)
self.parent = parent
self.some_handler = some_handler
self.some_Event = Some_Event
self.some_another_class = Some_class
def filename_to_txt(self,input_filename):
splitted = input_filename.split(".raw")
txt_filename = splitted[0] + splitted[1] + ".txt"
return txt_filename
How should I write unit test for that method?
It is static method in some class, but to test that firstly I have to make and instance of Some_panel class.
Do I really have to provide all arguments for __init__ method of Some_panel class?? Is there another better approach, for which I don't have to make an instance of that Some_panel class to test filename_to_txt method.
import unittest
from some_file import Some_panel
class TestSomething(unittest.TestCase):
def testname(self):
some_panel = Some_panel( ???? )
testfilename = "TestFilename.raw.001"
result = some_panel.filename_to_txt(input_filename = testfilename)
self.assertEqual(result, "TestFilename.001.txt", "Something is wrong")
If it's a static method, remove the self argument and decorate it with the #staticmethod decorator. You can then call it as a pure function using some_panel.filename_to_text(filename).
I think the best approach would be to stub out the SomePanel class.
class SomePanelStub(SomePanel):
def __init__(self):
self.parent = None
self.some_handler = None
self.some_Event = None
self.some_another_class = None
Now inside your unittest Class
def SetUp(self):
self.helper = SomePanelStub()
Now you have stubbed out SomePanel and can easily mock out any dependencies.