I need some help regarding modules & class and how to share data and methods.
I have been working on a Python program for a while and it has gotten too big to deal with in its original form which did not use classes. The program is about 6000 lines long and uses Tkinter with multiple screens. I figured the logical breakdown into classes would follow the user interface with each screen being its own class. Then I would have another class that stores all the "global" data.
So far, with everything still in one file, and it is working. However, I am now trying to break these classes out into separate modules by saving them in their own files. This is where things got really bad for me. I have read many tutorials and texts on classes and have seen a number of posts about my same problem here on Stack Overflow, but I still can't figure out what to do.
As my actual program is so big, I created a very simple program that shows what I need to do...
#main.py
class data:
def __init__(self):
self.A = "A"
self.B = "B"
self.C = "C"
self.All = ""
class module_1:
def __init__(self):
place_holder = "something"
def add_1_2(self):
d.All = d.A + d.B
print("new_string=", d.All)
class module_2:
def __init__(self):
place_holder = "something"
def combine_it_all(self):
m1.add_1_2()
d.All = d.All + d.C
d = data()
m1 = module_1()
m2 = module_2()
m2.combine_it_all()
print("d.All = ", d.All)
print("END OF PROGRAM")
The program above shows how I want to access data in other classes and use their methods. However, I also need to break out the program into modules so that they are much smaller and easier to work with. So, I tried to break out each class and put it in its own file (module) and now I don't know how to access data or methods in other classes which come from modules. Here are the breakdowns of each file...
#data_p.py
class data:
def __init__(self):
self.A = "A"
self.B = "B"
self.C = "C"
self.All = ""
#module_1_p.py
class module_1:
def __init__(self):
place_holder = "something"
def add_1_2(self):
d.All = d.A + d.B
print("new_string=", d.All)
#module_2_p.py
class module_2:
def __init__(self):
place_holder = "something"
def combine_it_all(self):
m1.add_1_2()
d.All = d.All + d.C
#main.py
from data_p import data
from module_1_p import module_1
from module_2_p import module_2
d = data()
m1 = module_1()
m2 = module_2()
m2.combine_it_all()
print("d.All = ", d.All)
print("END OF PROGRAM")
As you can see, there a problems with attributes and methods being addressed, but not yet instantiated. Unfortunately, I am getting up there in years, and certainly not the brightest programmer around, but I am hoping someone can show me how to make this simple example work so that I can fix my actual program.
The issue is classes are instantiated, but each instance has no idea about the other, one way to fix it is to pass references in the method calls.
It's probably possible to do better in the actual context, but with these changes, your example will work:
#module_1_p.py (line 5 changed)
class module_1:
def __init__(self):
place_holder = "something"
def add_1_2(self, d):
d.All = d.A + d.B
print("new_string=", d.All)
#module_2_p.py (lines 5 and 6 changed)
class module_2:
def __init__(self):
place_holder = "something"
def combine_it_all(self, m1, d):
m1.add_1_2(d)
d.All = d.All + d.C
#main.py (line 9 changed)
from data_p import data
from module_1_p import module_1
from module_2_p import module_2
d = data()
m1 = module_1()
m2 = module_2()
m2.combine_it_all(m1, d)
print("d.All = ", d.All)
print("END OF PROGRAM")
Related
I'm new to logging in python and I would like to save, other than the outcome of a long pipeline in the log file, also the parameters/class attributes of some of the instance created in the pipeline.
Ideally this should not pollute too much the code where the class is implemented.
Even better if the solution takes into account only the instance of the class and writes its attribute in the log file without touching at all the class implementation.
Any suggestion, or god practice advice?
--- Edit:
An unpolished and simplified version initial attempt (as asked in the comment) is the most obvious I could think of, and consist in adding a method that queries the attribute of the class in a string to be returned when the method is called:
In a python package with 2 modules main.py and a_class.py written as follows:
>> cat main.py
import logging
from a_class import MyClass
logging.basicConfig(filename='example.log',level=logging.DEBUG)
logging.warning('Print this to the console and save it to the log')
logging.info('Print this to the console')
o = MyClass()
o.attribute_1 = 1
o.attribute_2 = 3
o.attribute_3 = 'Spam'
logging.info(o.print_attributes())
and
>> cat a_class.py
class MyClass():
def __init__(self):
self.attribute_1 = 0
self.attribute_2 = 0
self.attribute_3 = 0
def print_attributes(self):
msg = '\nclass.attribute_1 {}\n'.format(self.attribute_1)
msg += 'class.attribute_2 {}\n'.format(self.attribute_2)
msg += 'class.attribute_3 {}\n'.format(self.attribute_3)
return msg
The example.log contains what I wanted, which is:
WARNING:root:Print this to the console and save it to the log
INFO:root:Print this to the console
INFO:root:
class.attribute_1 1
class.attribute_2 3
class.attribute_3 Spam
In reformulating the question, is there a way of doing the same query to the attribute of the class and send it to the log without adding any kind of print_attributes method in the class itself?
Use the inbuilt __dict__
class MyClass():
def __init__(self):
self.attribute_1 = 0
self.attribute_2 = 0
self.attribute_3 = 0
o = MyClass()
print o.__dict__
Outputs:
{'attribute_2': 0, 'attribute_3': 0, 'attribute_1': 0}
Use it in logging as you want to.
I'd suggest to implement __str__ or __repr__ for your class so that it would nicely show all the salient attribute values.
Then you can log instances as simple values: log.info("Now foo is %s", foo_instance).
A complete example:
class Donut(object):
def __init__(self, filling, icing):
self.filling = filling
self.icing = icing
def __repr__(self):
return 'Donut(filling=%r, icing=%r)' % (self.filling, self.icing)
donut = Donut('jelly', 'glaze')
import logging
logging.basicConfig()
logging.getLogger().warn('Carbs overload: one %s too much', donut)
Output:
2017-10-25 10:59:05,302 9265 WARNING Carbs overload: one Donut(filling='jelly', icing='glaze') too much
I agree with #Iguananaut that there is no magical way of doing this. However, the following may do the trick. It is better than the print_attributes method you wrote, IMO.
import logging
logging.basicConfig()
logger = logging.getLogger('ddd')
logger.setLevel(logging.DEBUG)
class A(object):
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
def __str__(self):
return "\n".join(["{} is {}".format(k, v)
for k, v in self.__dict__.iteritems()])
a = A(1, 2, 3)
logger.debug(a)
The result looks like this -
{12:43}~ ➭ python logging_attrib.py
DEBUG:ddd:a is 1
c is 3
b is 2
Please let me know what you think
Is it possible to have serializable static class variables or methods in python?
As an example suppose, I have the following code snippet:
import pickle
class Sample:
count = 0 # class variable
def __init__(self, a1=0, a2=0):
self.a = a1
self.b = a2
Sample.count += 1
#MAIN
f = open("t1.dat", "wb")
d = dict()
for i in range(10):
s = Sample(i, i*i)
d[i] = s
pickle.dump(d,f)
print "Sample.count = " + str(Sample.count)
f.close()
The output is:
Sample.count = 10
Now, I have another reader program similar to above:
import pickle
class Sample:
count = 0 # class variable
def __init__(self, a1=0, a2=0):
self.a = a1
self.b = a2
Sample.count += 1
#MAIN
f = open("t1.dat", "rb")
d = pickle.load(f)
print "Sample.count = " + str(Sample.count)
The output is:
Sample.count = 0
My question is:
How do I load the class variable from my file? In other words, how do I serialize a class variable? If directly not possible, is there any alternative? Please suggest.
Since class variable cannot be picked, as an alternative, I have used the code snippet in main part when reading from the file as below:
#MAIN
f = open("t1.dat", "rb")
d = pickle.load(f)
Sample.count = len(d.values())
print "Sample.count = " + str(Sample.count)
The output is now:
Sample.count = 10
Is it acceptable solution? Any other alternative?
Quoting the section on "What can be pickled and unpickled?"
Similarly, classes are pickled by named reference, so the same restrictions in the unpickling environment apply. Note that none of the class’s code or data is pickled, so in the following example the class attribute attr is not restored in the unpickling environment:
class Foo:
attr = 'a class attr'
picklestring = pickle.dumps(Foo)
So because attr, or in your case count, is part of the class definition, it never gets pickled. In your 'write' example, you're printing Sample.count which does exist but is not pickled in the first place.
You could store Sample.count in each instance as _count and put Sample.count = self._count. But remember that since your d is a dict, they may unpickle in any order. So essentially this won't work.
You'll need to add __setstate__ to your class customize the way it pickles and put in some flag value (like _count) which you then manipulate (via whatever logic works consistently) in __getstate__. (Edit: doesn't help with the given problem unless you store count in a global variable and access that in getstate and manipulate further each time an object is unpickled.)
Another potential workaround but yuck: Add a variable to your dict d so that it also gets pickled. When you read it back, restore with Sample.count = d['_count']. So before pickle.dump(d,f) when you pickle, do d['_count'] = Sample.count.
Important caveat: This is not actually allowing you to pickle Sample.count since what you're actually pickling (d) is a dictionary of Samples.
Edit: The Sample.count = len(d.values()) which you've put as a workaround is very specific to your use case and not to class attr's in general.
class LogicGate(object):
def __init__(self, n):
self.label = n
self.output = None # ????????????
def getOutput(self):
self.output = self.performGateLogic()
return self.output
def getLabel(self):
return self.label
class BinaryGate(LogicGate):
def __init__(self, n): # ?????????????????
LogicGate.__init__(self, n)
self.pinA = None # ??????????????
self.pinB = None # ??????????????
def getPinA(self):
return int(raw_input('Enter Pin A input for gate' + self.getLabel() + '-->'))
def getPinB(self):
return int(raw_input('Enter Pin A input for gate' + self.getLabel() + '-->'))
class UnaryGate(LogicGate):
def __init__(self, n): # ??????????????
LogicGate.__init__(self, n)
self.pin = None # ?????????????
def getPin(self):
return int(raw_input('Enter Pin input for gate' + self.getLabel() + '-->'))
class AndGate(BinaryGate):
def __init__(self, n): # ????????????
BinaryGate.__init__(self, n)
def performGateLogic(self):
a = self.getPinA()
b = self.getPinB()
if a == 1 and b == 1:
return 1
else:
return 0
This code belongs to Problem Solving with Algorithms and Date Structures.
When I remove the lines before the comment '# ????????', the code can run normally.
Why does the author write the code like this?
Whether is it a good code style?
Can I always remove these lines before the comment '# ????????' ?
The author writes the code like that because it is good practice to never have uninitialised members and class parents, static checkers moan if you do.
The reason that it is not good practice is for future maintainability - let us say that the base class, LogicGate, was to gain a new property - say propagation_delay and a new method that allowed simulations to called get_response_time which relied on the current output state and the required, possibly new, state. If all the code that was derived from that class did the correct initialisations then it would all work fine, without any changes. If you remove those lines and such a new method was introduced you would have to go back through all of the child classes adding them back in before your final class would work for that method, with the chance that you would miss one.
Daft as it sounds doing things properly now is actually future laziness - it only takes you seconds when you are creating a class to make sure everything is initialised - debugging an uninitialised class can take hours.
First:
The __init__ functions are the constructors of the classes, you can read about them here.
Second:
Your code will run without those lines but the question is why and is it ok to remove them?
For example if you remove the following init
class UnaryGate(LogicGate): # LogicGate is the superclass
def __init__(self, n):
LogicGate.__init__(self, n)
The constructor of the super-class LogicGate will be called directly.
Third:
Ok, so can we remove the self.xxx = None?
class BinaryGate(LogicGate):
def __init__(self, n):
LogicGate.__init__(self, n)
self.pinA = None
self.pinB = None
We could remove those 2 Lines too but consider this code
bg = BinaryGate("binaryGate1")
print bg.pinA
This would throw an error because pinA is undefined.
If you do not remove the self.pinA = None in __init__ the code will run and None will be printed.
I have created a simple renaming script but I would like to ask for some advice so that I can refine the coding as well as honing my python scripting. Below is a small portion of code for now...
Though this may not be an issue in my point of view, but other than the two functions I have stated below, I have came to realize that almost all my functions, they contains objects = cmds.ls(selection=True) Though I do not mind retyping over and over again but I do believe there is a better way to rectify this problem.
However, when I tried to make them global before the class function, it is able to run until when I tired to execute one of the functions, it prompts an error saying that global name 'objects' is not defined or 'objects are not defined' etc.
Pertaining to that, any suggestions?
class mainWindow(QDialog):
def __init__(self, parent=None):
super(mainWindow, self).__init__(parent)
self.resize(300,225)
self.initUI()
self.createConnections()
def searchReplace(self):
wordSearch = str(self.searchTxt.text())
wordReplace = str(self.replaceTxt.text())
objCnt = cmds.ls(sl=True, sn=True)
if len(objCnt) == 0:
self.searchTxt.clear()
self.replaceTxt.clear()
cmds.warning('Nothing is selected')
else:
for wordString in sorted(objCnt):
if wordSearch in wordString:
newWordString = wordString.replace(wordSearch, wordReplace)
cmds.rename(wordString, newWordString)
self.searchTxt.clear()
self.replaceTxt.clear()
print '%s' %wordString + " has changed to : " + "%s" %newWordString
def addPrefix(self):
objects = cmds.ls(selection=True)
pfx = str(self.prefixTxt.text())
for item in objects:
if pfx == "":
cmds.warning('No prefix values in the field')
else:
cmds.rename(item, pfx + "_" + item)
self.prefixTxt.clear()
print 'Prefix added: %s_' %pfx
def addSuffix(self):
objects = cmds.ls(selection=True)
sfx = str(self.suffixTxt.text())
for item in objects:
cmds.rename(item, item + "_" + sfx)
self.suffixTxt.clear()
print 'Suffix added: _%s' %sfx
def numPadding(self):
objects = pm.ls(selection=True)
num = self.numTxt.text()
padding = self.paddingTxt.text()
if num != "" and padding !="":
try:
for currentWordStr in objects:
pad = ("%%0%ii" % int(padding)) % int(num)
newWordStr = currentWordStr.rename(currentWordStr.name() + "_" + pad)
except Exception:
self.numTxt.clear()
self.paddingTxt.clear()
cmds.warning('Input numerical values only')
else:
cmds.warning('Entries of Num or Padding are empty')
def selectHierarchy(self):
sel = cmds.ls(selection = True)
selCnt = len(sel)
if int(selCnt) == 0:
cmds.warning('Nothing is selected')
else:
objHierarchy = cmds.listRelatives(ad=True, type='transform', fullPath=True)
cmds.select(sel, objHierarchy)
def clearHierarchy(self):
sel = cmds.ls(selection = True)
selCnt = len(sel)
if int(selCnt) != 0 :
objHierarchy = cmds.select(clear=True)
else:
cmds.warning('Selection is empty. Nothing to be cleared')
All right, I think I understand what you tried, going to take a shot at an answer.
First, take a look at the following posts, should get you up to speed on globals:
Using global variables in a function other than the one that created them (great, succinct summary)
Variable scope outside of classes (example with classes)
So, first off, you don't need to use the global keyword when first declaring objects outside of the class definition. So, instead of:
global objects
objects = cmds.ls(selection=True)
class mainWindow(QDialog):
...
You would do:
objects = cmds.ls(selection=True)
class mainWindow(QDialog):
...
Then, your functions can just refer to "objects". If you need to WRITE to objects from within your functions in the class, then you need to first use the global keyword (this code assumes objects was defined before the class):
def my_method(self):
global objects
objects = some_function()
That said, I'm not 100% sure how the above code is being invoked, so it's possible that something else is causing "objects" to be undefined.
You might be better served with a class attribute here. You could do this:
class mainWindow(QDialog):
objects = cmds.ls(selection=True)
def my_func(self):
for item in self.objects:
do_stuff()
Keep in mind that objects would be the same for all instances of mainWindow, and any updates to objects in one instance will affect all other instances. That should be fine from what I can tell, but you should definitely become familiar with instance vs. class vs. module.
Hope that helps!
UPDATE: Whoops, changed the class attribute in one place, but not the other in the last example. Updated the example, it should make way more sense now.
pI am working on a bit of code that does nothing important, but one of the things I am trying to make it do is call a function from another class, and the class name is pulled out of a list and put into a variable. Mind you I have literally just learned python over the last 2 weeks, and barely know my way around how to program.
What I believe that this should do is when getattr() is called, it will pass the attribute 'run_question' that is contained in the respective class with the same name as what is in question_type, and then pass it onto 'running_question'. I know there are probably better ways to do what I am attempting, but I want to know why this method doesn't work how I think it should.
#! /usr/bin/python
rom random import randrange
class QuestionRunner(object):
def __init__(self):
##initialize score to zero
self.score = 0
##initialize class with the types of questions
self.questiontypes = ['Addition', 'Subtraction', 'Division', 'Multiplication']
##randomly selects question type from self.questiontypes list
def random_type(self):
type = self.questiontypes[randrange(0, 4)]
return type
##question function runner, runs question function from self
def run_questions(self):
try:
question_type = self.random_type()
running_question = getattr(question_type, 'run_question' )
except AttributeError:
print question_type
print "Attribute error:Attribute not found"
else: running_question()
class Question(object):
pass
class Multiplication(Question):
def run_question(self):
print "*"
class Division(Question):
def run_question(self):
print "/"
class Subtraction(Question):
def run_question(self):
print "-"
class Addition(Question):
def run_question(self):
print "+"
test = QuestionRunner()
test.run_questions()
This outputs:
[david#leonid mathtest] :( $ python mathtest.py
Division
Attribute error:Attribute not found
[david#leonid mathtest] :) $
Which indicates that I am not getting the run_question attribute as I expect.
I should note that when I put the functions into the QuestionRunner class in the following way, everything works as expected. The main reason I am using classes where it really isn't needed it to actually get a good grasp of how to make them do what I want.
#! /usr/bin/python
from random import randrange
class QuestionRunner(object):
def __init__(self):
##initialize score to zero
self.score = 0
##initialize class with the types of questions
self.questiontypes = ['addition', 'subtraction', 'division', 'multiplication']
##randomly selects question type from self.questiontypes list
def random_type(self):
type = self.questiontypes[randrange(0, 4)]
return type
##question function runner, runs question function from self
def run_questions(self):
try:
question_type = self.random_type()
running_question = getattr(self, question_type)
except AttributeError:
exit(1)
else: running_question()
def multiplication(self):
print "*"
def division(self):
print "/"
def addition(self):
print "+"
def subtraction(self):
print "-"
test = QuestionRunner()
test.run_questions()
Any help on why this isn't working would be great, and I appreciate it greatly.
Any help on why this isn't working would be great, and I appreciate it greatly.
Ah, I have found out the missing concept that was causing my logic to be faulty. I assumed that I could pass the name of an object to getattr, when in reality I have to pass the object itself.