Python: How to Encapsulate a Variable Across Multiple Classes? - python

I have a script using threaded timers that manipulates 2 common lists at random.
Because the class instances manipulate the lists on threaded timers, I cannot pass the variables to the classes & back.
…All instances of the classes need to manipulate a single, up to date list.
Because of that, the scope of those lists are set to global. However, I need the scope to be at the class level, yet be manipulated by multiple classes.
To clarify...
Here's the basic program structure:
Global list variable_1
Global list variable_2
class MasterClass:
# this creates instances of the threaded classes.
There are 50+ instances of MasterClass creating thousands
of instances of ThreadedClass1, 2, & 3. All manipulate
global list variables 1 & 2.
class ThreadedClass1:
# threaded classes manipulate global list variables 1 & 2 on random timers.
class ThreadedClass2:
class ThreadedClass3:
The problem: For each instance of MasterClass I need a separate list variable 1 & 2. Each instance of ThreadedClasses called by that instance of MasterClass must manipulate only the list variables owned by that instance of MasterClass.
Basically I need the equivalent of a global list variable, but I need it to be encapsulated by an instance of MasterClass, and be manipulated by any instance of ThreadedClasses called by that instance of MasterClass only.
How's this done?

Try to pass instance of MasterClass to every produced instance of ThreadedClasses.
Then, define thread save methods in MasterClass, that will perform manipulation with your variable_1, variable_2. ThreadedClasses shall not touch this lists directly, only by calling those methods.
Small example (check subclassing from object):
import threading
class ThreadedClassBase(object):
def __init__(self, master, *args, **kwargs):
self.master = master
def do_something(self):
self.master.append(1, 'some_value')
value = self.master.getitem(1, 0)
class ThreadedClass1(ThreadedClassBase):
def __init__(self, *args, **kwargs):
super(ThreadedClass1, self).__init__(*args, **kwargs)
# ...
# same for ThreadedClass2, 3
class MasterClass(object):
def __init__(self, *args, **kwargs):
self.variable_1 = list()
self.variable_2 = list()
self.lock = threading.Lock()
for i in range(50):
ThreadedClass1(master=self)
# create new thread
def append(list_nb, value):
with self.lock:
getattr('variable_' + list_nb).append(value)
def getitem(list_nb, index):
with self.lock:
return getattr('variable_' + list_nb)[index]

If I understand correctly, you should be able to make them instance variables of MasterClass and pass them into the constructors.
eg.
class MasterClass:
def __init__(self):
self.variable_1 = [...]
self.variable_2 = [...]
self.tc1 = ThreadedClass1(self.variable_1, self.variable_2)
self.tc2 = ThreadedClass2(self.variable_1, self.variable_2)
self.tc3 = ThreadedClass3(self.variable_1, self.variable_2)
Alternatively pass the whole instance in
class MasterClass:
def __init__(self):
self.variable_1 = [...]
self.variable_2 = [...]
self.tc1 = ThreadedClass1(self)
self.tc2 = ThreadedClass2(self)
self.tc3 = ThreadedClass3(self)
class ThreadedClass1:
def __init__(self, parent):
self.mc = parent
etc.

Related

Sharing data between classes without objects

I have an issue with sharing data between classes. I guess how it works with objects, but I need to share data without creating objects, for example:
class First_class:
def __init__(self):
var = 1 ##create some random variable
class Second_class:
def on_click(self):
print(var) ##working with variable from First_class
First_class()
Second_class()
Some ideas on how to make it or some better solution? btw I use Tkinter and these classes simulate frames with some widgets, so more specifics:
class Frame1:
def __init__(self):
name = Enter(root)
class Frame2:
def on_click(self):
print(name.get())
Frame1()
Frame2()
Set the variable as a class property instead of under init, as the class would have to be initialised in order to get that value.
class First_class:
var = 1
def __init__(self):
pass
class Second_class:
def on_click(self):
print(First_class.var)

Store instance of class A in instance of class B

I have a question which is more regarding OOP in general rather than python specific.
Is ist possible to store instances of ClassA in instance of ClassB without a specific method, i.e. by some kind of inheritance.
Example: let's say I have one Model class and one Variable class
class Model():
def __init__(self):
self.vars = []
def _update_vars(self,Variable):
self.vars.append(Variable)
class Variable(Model):
def __init__(self,**kwargs):
self.__dict__.update(kwargs)
Is it now possible to call _update_vars whenever an instance of variable is being created.
So if I do something like this:
mdl = Model()
varA = Variable(...)
varB = Variable(...)
that mdl.vars would now include varA and varB.
I know that I could easily do this by passing the variables as an argument to a "public" method of Model. So I am not looking for
mdl.update_vars(varA)
So my two questions are:
is this possible?
if yes: would this very non-standard OOP programming?
Thanks for your help!
That's not how class inheritance is supposed to work. You only want to inherit something if the child class is going to make use of a good amount of the attributes/methods within the parent class. If the child class has a markedly different structure it should be a class of its own.
In either case, as mentioned by #jasonharper, at some point you would need to give direction as to which Variable instance belongs in which Model instance, so you're likely to end up with something like these:
varA = Variable(mdl, ...)
# or this
mdl.varA = Variable(...)
With the first way, you would maintain the method on your Variable class:
class Foo:
def __init__(self):
self.vars = []
class Bar:
def __init__(self, foo_instance, **kwargs):
foo_instance.vars.append(self)
f = Foo()
b = Bar(f, hello='hey')
f.vars
# [<__main__.Bar object at 0x03F6B4B0>]
With the second way, you can append the Variable instances into a list each time it's added:
class Foo:
def __init__(self):
self.vars = []
def __setattr__(self, name, val):
self.__dict__.update({name: val})
if not name == 'vars': # to prevent a recursive loop
self.vars.append(val)
f = Foo()
f.vars
# []
f.a = 'bar'
f.vars
# ['bar']
Of course, an easier way would be to just look directly into the __dict__ each time you want vars:
class Bar:
#property
def vars(self):
# Or you can return .items() if you want both the name and the value
return list(self.__dict__.values())
b = Bar()
b.a = 'hello'
b.vars
# ['hello']
Both of these will work the same even if you assigned the attributes with your own class instances.
You can use super() for this and pass the instance to the parent
class Model():
vars = []
def __init__(self, other=None):
if other:
self.vars.append(other)
class Variable(Model):
def __init__(self, a):
self.a = a
super().__init__(self)
mdl = Model()
varA = Variable(3)
varB = Variable(4)
print(mdl.vars)

Initialize variable if class method is run

I have a class, like
class D:
def __init__(self):
"""some variables"""
def foo(self):
"""generates lots of data"""
I would like to be able to store all of the data that foo creates within the instance of the class that foo is being called from. Almost like creating a new initialization variable, but only once the method is called. For if the user never calls foo, no need to have generated the data to begin with.
Thanks!
How about to make a flag which will say if data was already generated?
class D:
def __init__(self):
self.already_generated = False
"""some variables"""
def foo(self):
"""generates lots of data"""
if not already_generated:
self.generate()...
already_generated = True
def generate(self,...):
Not quite sure if this is what you're trying to do, but if you want a class method to generate data that can be accessed from that instance you can put it into a data structure that is a member of that class:
class D:
def __init__(self):
#class member variables here
self.fooArray = []
def foo(self):
#insert your data to self.fooArray here, eg:
for i in range(1, 10000):
self.fooArray.append(i)

Accessing Class variables from another class

How do you access an instance in an object and pass it to another 'main' object? I'm working with a parser for a file that parses different tags, INDI(individual), BIRT(event), FAMS(spouse), FAMC(children)
Basically there are three classes: Person, Event, Family
class Person():
def __init__(self, ref):
self._id = ref
self._birth : None
def addBirth(self, event):
self._birth: event
class Event():
def __init__(self, ref):
self._id = ref
self._event = None
def addEvent(self, event):
self._event = event
#**event = ['12 Jul 1997', 'Seattle, WA'] (this is generated from a function outside a class)
I want to transfer self._event from the Event class into addBirth method to add it into my person class. I have little knowledge on how classes and class inhertiances work. Please help!
If I understand your question, you want to pass an (for example) Event object to an instance of Person?
Honestly, I don't understand the intent of your code, but you probably just need to pass self from one class instance to the other class instance.
self references the current instance.
class Person:
def __init__(self):
self._events = []
def add_event(self, event)
self._events.append(event)
class Event:
def add_to_person(self, person):
person.add_event(self)
The most proper way to handle situations like this is to use getter and setter methods; data encapsulation is important in OO programming. I don't always see this done in Python where I think it should, as compared to other languages. It simply means to add methods to your classes who sole purpose are to return args to a caller, or modify args from a caller. For example
Say you have class A and B, and class B (caller) wants to use a variable x from class A. Then class A should provide a getter interface to handle such situations. Setting you work the same:
class class_A():
def __init__(self, init_args):
x = 0
def someMethod():
doStuff()
def getX():
return x
def setX(val):
x = val
class class_B():
def init(self):
init_args = stuff
A = class_A(init_args)
x = class_A.getX()
def someOtherMethod():
doStuff()
So if class B wanted the x property of an instance object A of class class_A, B just needs to call the getter method.
As far as passing instances of objects themselves, say if you wanted A to pass an already-created instance object of itself to a method in class B, then indeed, you simply would pass self.

Inheritance in Python, init method overrriding

I'm trying to understand inheritance in Python. I have 4 different kind of logs that I want to process: cpu, ram, net and disk usage
I decided to implement this with classes, as they're formally the same except for the log file reading and the data type for the data. I have a the following code (log object is a logging object instance of a custom logging class)
class LogFile():
def __init__(self,log_file):
self._log_file=log_file
self.validate_log()
def validate_log(self):
try:
with open(self._log_file) as dummy_log_file:
pass
except IOError as e:
log.log_error(str(e[0])+' '+e[1]+' for log file '+self._log_file)
class Data(LogFile):
def __init__(self,log_file):
LogFile.__init__(self, log_file)
self._data=''
def get_data(self):
return self._data
def set_data(self,data):
self._data=data
def validate_data(self):
if self._data == '':
log.log_debug("Empty data list")
class DataCPU(Data):
def read_log(self):
self.validate_log()
reading and writing to LIST stuff
return LIST
class DataRAM(Data):
def read_log(self):
self.validate_log()
reading and writing to LIST stuff
return LIST
class DataNET(Data):
Now I want my DataNET class to be a object of the Data Class with some more attributes, in particular a dictionary for every one of the interfaces. How can I override the __init__() method to be the same as the Data.__init__() but adding self.dict={} without copying the Data builder? This is, without explicitly specifing the DataNet objects do have a ._data attribute, but inherited from Data.
Just call the Data.__init__() method from DataNET.__init__(), then set self._data = {}:
class DataNET(Data):
def __init__(self, logfile):
Data.__init__(self, logfile)
self._data = {}
Now whatever Data.__init__() does to self happens first, leaving your DataNET initializer to add new attributes or override attributes set by the parent initializer.
In Python 3 classes are already new-style, but if this is Python 2, I'd add object as a base class to LogFile() to make it new-style too:
class LogFile(object):
after which you can use super() to automatically look up the parent __init__ method to call; this has the advantage that in a more complex cooperative inheritance scheme the right methods are invoked in the right order:
class Data(LogFile):
def __init__(self,log_file):
super(Data, self).__init__(log_file)
self._data = ''
class DataNET(Data):
def __init__(self, logfile):
super(DataNET, self).__init__(logfile)
self._data = {}
super() provides you with bound methods, so you don't need to pass in self as an argument to __init__ in that case. In Python 3, you can omit the arguments to super() altogether:
class Data(LogFile):
def __init__(self,log_file):
super().__init__(log_file)
self._data = ''
class DataNET(Data):
def __init__(self, logfile):
super().__init__(logfile)
self._data = {}
Use new style classes (inherit from object) - change definition of LogFile to:
class LogFile(object):
and init method of Data to:
def __init__(self, log_file):
super(Data, self).__init__(log_file)
self._data = ''
Then you can define DataNET as:
class DataNET(Data):
def __init__(self, log_file):
super(DataNET, self).__init__(log_file)
self.dict = {}

Categories