I tried this inheritance code and got an unexpected output. Kindly guide me where is my fault.
Child.py
from ParentClass import Parent
class ChildImp(Parent):
var2 = 200
def __init__(self):
Parent.__init__(self, 8, 3)
def getData(self):
self.var1 + self.Sub() + self.Add()
obj = ChildImp()
print(obj.getData())
ParentClass.py
class Parent:
var1 = 100
def __init__(self, a, b):
self.firstNUm = a
self.secondNUm = b
def Add(self):
return self.firstNUm + self.secondNUm
def Sub(self):
return self.firstNUm - self.secondNUm
obj1 = Parent(4, 6)
print(obj1.Add())
obj2 = Parent(9, 2)
print(obj2.Sub())
output:
10
7
None
Process finished with exit code 0
Where does this 10 and 7 come from?
Why there is a None in the output?
The output is from each of the following calls
10 # print(obj1.Add())
7 # print(obj2.Sub())
None # print(obj.getData())
note that Add and Sub will return the computed value, btu getData has no return so will implicitly return None hence the last output.
Note that the reason those Add and Sub lines are executing is because of the import statement. If you only want those to run when that script is directly invoked you would modify that block to
if __name__ == '__main__':
obj1 = Parent(4, 6)
print(obj1.Add())
obj2 = Parent(9, 2)
print(obj2.Sub())
Output is from the 3 print statements.
print(obj1.Add()) # prints 10
print(obj2.Sub()) # prints 7
print(obj.getData()) # prints None
Need a return on ChildImp getData() function otherwise returns None.
from ParentClass import Parent
class ChildImp(Parent):
var2 = 200
def __init__(self):
Parent.__init__(self, 8, 3)
def getData(self):
return self.var1 + self.Sub() + self.Add() # <== added return
obj = ChildImp()
print(obj.getData())
Also good practice to add if name == __main__ check in the parent class so only the main in the class called is executed.
# ParentClass.py
class Parent:
...
if __name__ == '__main__':
obj1 = Parent(4, 6)
print(obj1.Add())
obj2 = Parent(9, 2)
print(obj2.Sub())
10 and 7 are the return values of the Add and Sub methods
The None is there because getData does not return anything
For all three values there is a print call.
Related
I defined a class named Experiment for the results of some lab experiments I am conducting. The idea was to create a sort of database: if I add an experiment, this will be pickled to a db before at exit and reloaded (and added to the class registry) at startup.
My class definition is:
class IterRegistry(type):
def __iter__(cls):
return iter(cls._registry)
class Experiment(metaclass=IterRegistry):
_registry = []
counter = 0
def __init__(self, name, pathprotocol, protocol_struct, pathresult, wallA, wallB, wallC):
hashdat = fn.hashfile(pathresult)
hashpro = fn.hashfile(pathprotocol)
chk = fn.checkhash(hashdat)
if chk:
raise RuntimeError("The same experiment has already been added")
self._registry.append(self)
self.name = name
[...]
While fn.checkhash is a function that checks the hashes of the files containing the results:
def checkhash(hashdat):
for exp in cl.Experiment:
if exp.hashdat == hashdat:
return exp
return False
So that if I add a previously added experiment, this won't be overwritten.
Is it possible to somehow return the existing instance if already existant instead of raising an error? (I know in __init__ block it is not possible)
You can use __new__ if you want to customize the creation instead of just initializing in newly created object:
class Experiment(metaclass=IterRegistry):
_registry = []
counter = 0
def __new__(cls, name, pathprotocol, protocol_struct, pathresult, wallA, wallB, wallC):
hashdat = fn.hashfile(pathresult)
hashpro = fn.hashfile(pathprotocol)
chk = fn.checkhash(hashdat)
if chk: # already added, just return previous instance
return chk
self = object.__new__(cls) # create a new uninitialized instance
self._registry.append(self) # register and initialize it
self.name = name
[...]
return self # return the new registered instance
Try to do it this way (very simplified example):
class A:
registry = {}
def __init__(self, x):
self.x = x
#classmethod
def create_item(cls, x):
try:
return cls.registry[x]
except KeyError:
new_item = cls(x)
cls.registry[x] = new_item
return new_item
A.create_item(1)
A.create_item(2)
A.create_item(2) # doesn't add new item, but returns already existing one
After four years of the question, I got here and Serge Ballesta's answer helped me. I created this example with an easier syntax.
If base is None, it will always return the first object created.
class MyClass:
instances = []
def __new__(cls, base=None):
if len(MyClass.instances) == 0:
self = object.__new__(cls)
MyClass.instances.append(self)
if base is None:
return MyClass.instances[0]
else:
self = object.__new__(cls)
MyClass.instances.append(self)
# self.__init__(base)
return self
def __init__(self, base=None):
print("Received base = %s " % str(base))
print("Number of instances = %d" % len(self.instances))
self.base = base
R1 = MyClass("apple")
R2 = MyClass()
R3 = MyClass("banana")
R4 = MyClass()
R5 = MyClass("apple")
print(id(R1), R1.base)
print(id(R2), R2.base)
print(id(R3), R3.base)
print(id(R4), R4.base)
print(id(R5), R5.base)
print("R2 == R4 ? %s" % (R2 == R4))
print("R1 == R5 ? %s" % (R1 == R5))
It gives us the result
Received base = apple
Number of instances = 2
Received base = None
Number of instances = 2
Received base = banana
Number of instances = 3
Received base = None
Number of instances = 3
Received base = apple
Number of instances = 4
2167043940208 apple
2167043940256 None
2167043939968 banana
2167043940256 None
2167043939872 apple
R2 == R4 ? True
R1 == R5 ? False
Is nice to know that __init__ will be always called before the return of the __new__, even if you don't call it (in commented part) or you return an object that already exists.
I have three classes which I want to call and save them dependant on list contents. I want to figure out how to call two classes and call values within tow classes and save them, when I want to call them later, that classes remember and display what a user has been selected.
The code:
_list = [0, 1, 2, 3, 4]
class Data(object):
def __init__(self):
self.ID = 'Data_id'
self.first = 'Data_1'
self.second = 'Data_2'
self.third = 'Data_3'
class Value(object):
def __init__(self):
self.ID = 'Value_id'
self.first = 'Value_1'
self.second = 'Value_2'
self.third = 'Value_3'
class Base(object):
def __init__(self):
self.item = -1
if self.item == 1 or self.item == 2:
self.first_class = Data()
self.second_class = Value()
print('Item:', self.item, 'Class_ID: {}'.format( self.first_class.second))
print('Item:', self.item, 'Class_ID: {}'.format( self.second_class.first))
elif self.item == 2 or self.item == 4:
self.first_class = Data()
self.second_class = Value()
print('Item:', self.item, 'Class_ID: {}'.format( self.first_class.first))
print('Item:', self.item, 'Class_ID: {}'.format( self.second_class.third))
else:
self.first_class = Data()
self.second_class = Value()
print('Item:', self.item, 'Class_ID: {}'.format( self.first_class.ID))
print('Item:', self.item, 'Class_ID: {}'.format( self.second_class.ID))
run = Base()
for i in _list:
run.item = _list[i]
_list values changes every time, it could be None or any int number from 0 to 9 for example. The idea is to call two classes Data and Value every time an item from list is initialized. Then the values within the classes are called for example item 1 is called then the tow classes run and values are called.
print('Item:', self.item, 'Class_ID: {}'.format( self.first_class.first))
print('Item:', self.item, 'Class_ID: {}'.format( self.second_class.third))
After execution of for-loop, user want to run following code or to know which values from which class is called at item = 3
run.item = _list[3]
How can I achieve this behaviour?
I appreciate any help. In advance thanks.
___init___ is a special method which supposed to run exactly once when the class is initialized. When you call run = Base() the logic runs, self.item will be evaluated as -1
What your class does is basically this:
controls = [0,1]
class Controlled:
def __init__(self):
self.param = -1
if self.param == 0:
print(0)
elif self.param == 1:
print(1)
else:
print('This is not unexpected')
c = Controlled()
for p in controls:
c.param = p
If you run Controlled(), the self.param will be -1 every time during the initialization, the else branch will print the This is not unexpected message. The
for p in controls:
c.param = p
part basically just updates the class attribute but does not call anything, nothing will be printed or executed.
I think you wanted to implement something like this:
controls = [0,1]
class Controlled:
def __init__(self, param=-1):
self.param = param
if self.param == 0:
print(0)
elif self.param == 1:
print(1)
else:
print('This is not unexpected')
results = []
for p in controls:
results.append(Controlled(p))
When you call __init__(self, param=-1), the given parameter (p) will be taken into account and the init logic will run based on the given parameter.
Using Python 2.7.10, I have this script:
#!/usr/bin/python
#Do `sudo pip install boto3` first
import boto3
import json
def generate(key, value):
"""
Creates a nicely formatted Key(Value) item for output
"""
return '{}={}'.format(key, value)
def main():
ec2 = boto3.resource('ec2', region_name="us-west-2")
volumes = ec2.volumes.all()
for vol in volumes:
#print(vol.__str__())
#print(vol.__dict__)
print vol
# vol object has many attributes, which can be another class object.
# For ex:
#vol.volume_id),
#vol.availability_zone),
#vol.volume_type),
# only process when there are tags to process
# HERE: tags is another object which can contain a dict/list
#if vol.tags:
# for _ in vol.tags:
# # Get all of the tags
# output_parts.extend([
# generate(_.get('Key'), _.get('Value')),
# ])
# output everything at once.
print ','.join(output_parts)
if __name__ == '__main__':
main()
Is there a single function which can recursively print all the object's attributes using a single call? How can I print the values of val.xxxxx and val.tags.xxxx in one call.
I tried printing the object using .__dict__ or .__str__() but it didn't help.
Here's a function that you can drop into the str for your object. You can also call it directly, passing in the object you wish to print. The function returns a string that represents a nicely formatted walk of your object's contents.
Credit goes to #ruud from forum.pythonistacafe.com for coming up with this solution.
def obj_to_string(obj, extra=' '):
return str(obj.__class__) + '\n' + '\n'.join(
(extra + (str(item) + ' = ' +
(obj_to_string(obj.__dict__[item], extra + ' ') if hasattr(obj.__dict__[item], '__dict__') else str(
obj.__dict__[item])))
for item in sorted(obj.__dict__)))
class A():
def __init__(self):
self.attr1 = 1
self.attr2 = 2
self.attr3 = 'three'
class B():
def __init__(self):
self.a = A()
self.attr10 = 10
self.attrx = 'x'
class C():
def __init__(self):
self.b = B()
class X():
def __init__(self):
self.abc = 'abc'
self.attr12 = 12
self.c = C()
def __str__(self):
return obj_to_string(self)
x = X()
print(x)
Output:
<class '__main__.X'>
abc = abc
attr12 = 12
c = <class '__main__.C'>
b = <class '__main__.B'>
a = <class '__main__.A'>
attr1 = 1
attr2 = 2
attr3 = three
attr10 = 10
attrx = x
A few days ago, I asked about passing values between instances of classes here is my post here
That problem was easy to fix especially the movement.
But now I can't pass objects between methods.
Example code:
class MyClassA(object):
def function1(self):
entry = input("Insert a value ::: ")
b = MyClassB(entry) #To pass the variable entry to class MyClassB
d = MyClassB()
d.function2()
c = MyClassC(b.k) #Initializied MyClassC to be ready for receive the value p
self.x = d.f #To get back the value k from MyClassB function2()
print(self.x)
self.x1 = c.p #To get back the value k from MyClassC
print(self.x1)
class MyClassB(object):
def __init__(self,M):
self.f = M
self.k = 0
def function2(self):
self.k = self.f * 10 # k will contain (the value entry from MyClassA *10)
c = MyClassC(self.k) #To pass variable k to class MyClassC
class MyClassC(object):
def __init__(self,passedVar):
self.p = passedVar + 0.1 # p will contain (the value entry from MyClassB + 0.1)
h = MyClassA()
h.function1()
Otherwise whenever i try to work with instanace it gonna work right but between methods not that such fixed.
as last time this my code should give this way of result :
Insert a value ::: 9 (assume the user typed 9 here)
so the output should be:
90
90.1
here my code complation say
d = MyClassB()
TypeError: __init__() takes exactly 2 arguments (1 given)
Any way I can fix my code > It doesn't need to work only with instances; I need some function inside some class
You need to pass something to the constructor for MyClassb()...
Ok My problem is
d = MyClassB()
d.function2()
Statu is Fixed now, i got the way !!!
Correct code is this way :
class MyClassA(object):
def function1(self):
entry = input("Insert a value ::: ")
b = MyClassB(entry) #To pass the variable entry to class MyClassB
b.function2()
c = MyClassC(b.k) #Initializied MyClassC to be ready for receive the value p
self.x = b.f #To get back the value k from MyClassB function2()
print(self.x)
self.x1 = c.p #To get back the value k from MyClassC
print(self.x1)
class MyClassB(object):
def __init__(self,M):
self.f = M
def function2(self):
self.k = self.f * 10 # k will contain (the value entry from MyClassA *10)
c = MyClassC(self.k) #To pass variable k to class MyClassC
class MyClassC(object):
def __init__(self,passedVar):
self.p = passedVar + 0.1 # p will contain (the value entry from MyClassB + 0.1)
h = MyClassA()
h.function1()
I am working on an application where variables get initialized to default values.
The user can change those values at any time. It should be possible for the user to reset some or all of the variables to default at any time.
How is the best way of going about this?
This would be a solution, but I have a feeling that it is suboptimal. Can you tell me if my feeling is correct and how I can do it better?
A_DEFAULT = "A_def"
B_DEFAULT = "B_def"
C_DEFAULT = "C_def"
class BusinessLogic_1(object):
def __init__(self):
self.setVariablesToDefault()
def setVariablesToDefault(self, variableNames=None):
# pass None to set all Variables to default
variableNames = variableNames or ["A","B","C"]
if "A" in variableNames:
self.A = A_DEFAULT
if "B" in variableNames:
self.B = B_DEFAULT
if "C" in variableNames:
self.C = C_DEFAULT
def printVariables(self):
print "A: %s, B: %s, C: %s" % (self.A, self.B, self.C)
if __name__ == "__main__":
print "0: Initialize"
businessLogic_1 = BusinessLogic_1()
businessLogic_1.printVariables()
print "Change A,B,C and then reset A,C"
businessLogic_1.A = "A_new"
businessLogic_1.B = "B_new"
businessLogic_1.C = "C_new"
businessLogic_1.printVariables()
This might be a solution.
It's a way to pass names of variables as strings and then to access them via these names.
Objects can have more than one name simultaneously, but names always point to a single object (at a given point of time).
class classWithVariablesThatHaveDefaultValues(object):
def __init__(self, some, example, variables, defaultValuesDict):
self.some = some
self.example = example
self.variables = variables
self.dictionaryWithDefaultValues = defaultValuesDict
def resetValues(self, *listOfVariables):
for variable in listOfVariables:
for key in self.dictionaryWithDefaultValues:
if key == variable:
vars(self)[key] = self.dictionaryWithDefaultValues[key]
if __name__ == "__main__":
defaultValuesDict = {"some":4, "example":5, "variables":6}
exampleObject = classWithVariablesThatHaveDefaultValues(1, 2, 3, defaultValuesDict)
exampleObject.some = 15
print exampleObject.some, exampleObject.example, exampleObject.variables
exampleObject.resetValues("example", "some")
print exampleObject.some, exampleObject.example, exampleObject.variables
The Output is:
15 2 3
4 5 3
You can achieve it in a manner very similar to how decimal.localcontext() works, but depending on your use case, this may not be suitable. This will allow you to manipulate and call any methods on the original object reflecting the updated values, and at the end of the with block, reset them to the values upon entry.
from contextlib import contextmanager
class A(object):
def __init__(self):
self.a = 3
self.b = 5
def display(self):
return self.a, self.b
a = A()
#contextmanager
def mycontext(obj):
old_a = obj.a
old_b = obj.b
yield obj
obj.a = old_a
obj.b = old_b
print 'before:', a.display()
with mycontext(a) as obj:
print 'entered:', a.display()
a.a = 3
a.b = 7
print 'changed:', a.display()
print 'hopefully original', a.display()
before: (3, 5)
entered: (3, 5)
changed: (3, 7)
hopefully original (3, 5)