Composite and Hierarchy without instance shared variables - python

I've written a good chunk of code that relies heavily on both inheritances and composition. Now i've run into a problem where my hierarchy classes need variables to be shared to work between each other, but that causes the composited classes to be shared too, meaning my separate instances of these classes will share values where i don't want them to. I obviously suck at explaining this with words so i wrote some code.
class First(object):
def __init__(self):
self.subvar1 = 0
self.subvar2 = 10
class Second(object):
variable3 = First()
class Third(Second):
def __init__(self):
self.variable4 = self.variable3.subvar2
Firstinstance = Third()
Secondinstance = Third()
Firstinstance.variable3.subvar1 = 50
Secondinstance.variable3.subvar2 = 0
print Firstinstance.variable3.subvar2 #<-prints 0 but i want 10
print Secondinstance.variable3.subvar1 #<-prints 50 but i want 0
Except for dumping the hierarchy system and writing one massive class where i can prevent composited classes from having their valued shared, is there any other way for me to work around this?

The problem comes from Second.variable3 being a class attribute - that is, shared by all instances of the class and it's subclasses. You want:
class Second(object):
def __init__(self):
self.variable3 = First()
class Third(Second):
def __init__(self):
super(Third, self).__init__()
self.variable4 = self.variable3.subvar2
which yields the desired results.

One way is to add
self.variable3 = First()
into the __init__ of Third, so you can keep the First and Second unchanged and prevent these values to be shared.

Related

Python Create Child objects in parent init with access to parent attributes

I want to create a parent object which creates its children and its children need to have access to one of the parent attributes.
I have found ways to create children inside a parent and ways to have access to one of the parent attributes, but no way to do both at the same time.
class Parent():
def __init__(self,number):
self.number = number
self.children = [Child(0), Child(2)]
class Child(Parent):
def __init__(self, number2):
self.number2 = number2
def printNumber(self):
print(self.number+self.number2)
I expect:
par = Parent(3)
par.children[0].printNumber()
<3>
par.children[1].printNumber()
<5>
But, this will crash because par.children[0].number does not exist.
Ok, I have to say that this does not seem an appropriate use of inheritance. Inheritance should be used to provide a multiple set of attributes and methods to different classes with things in common. Calling a child from a parent on initialization seems, a bit doubtful.
Still, I'm guessing there is a wider reason for this than this example. As such, I can offer a solution that does not require Child to use super() (as specified in the comments).
This solution has a caveat, though, and a big one. self.number becomes a class method and, as such, if a new Parent is instantiated with a diferent number value, it would change previously instantiated Parents.
So here it goes:
class Parent():
number = 0
def __init__(self, number):
self._change_number(number)
self.children = [Child(0), Child(2)]
#classmethod
def _change_number(cls, number):
cls.number = number
class Child(Parent):
def __init__(self, number2):
self.number2 = number2
def printNumber(self):
print(self.number+self.number2)
par = Parent(3)
par.children[0].printNumber()
# 3
par.children[1].printNumber()
# 5
Again be aware that number is a class variable!!. Then, continuing the script with:
pir = Parent(5) # Different instance
# changes the values in the previous instance par
par.children[0].printNumber()
# 5
par.children[1].printNumber()
# 7
I cannot see any other solution for this to work, as, without calling super from the Child there is no other way to initialize the self.number attribute if it is not a class one.
If you don't want this to happen, one option would be to calculate self.number2 at __init__ time. Then the value would be safely stored without more problem. In this case, Child would look like:
class Child(Parent):
def __init__(self, number2):
self.number2 = self.number+number2
def printNumber(self):
print(self.number2)
And a new instance would not affect the previous one:
par = Parent(3)
par.children[0].printNumber()
# 3
par.children[1].printNumber()
# 5
pir = Parent(5) # Different instance
# DOES NOT change the values in the previous instance par
par.children[0].printNumber()
# 3
par.children[1].printNumber()
# 5

Python inheritance not working as I expected

I have a class -
class Start(object):
def __init__(self):
self.flag = False
self.my_list = []
def set_up(self):
self.flag = True
def end_set_up(self):
self.my_list = [i*2 for i in self.my_list]
And another class which inherits from this class -
class Process(Start):
def __init__(self):
super(Process, self).__init__()
def check_flag(self):
if self.flag:
self.my_list = range(1, 10)
And in the third class, I want to do some operations on my_list
class Operation(Start):
def __init__(self):
super(Operation, self).__init__()
def perform_op(self):
self.my_list = [i*2 for i in self.my_list]
Now these classes are used in a code snippet as -
start_ob = Start()
start_ob.set_up()
process_ob = Process()
process_ob.check_flag()
op_ob = Operation()
op_ob.perform_op()
My understanding of classes is not that strong. What I thought of achieving with this was -
Set up class Start()
Inherit flag from class Start() into Process() which should be True now since I called set_up() function here start_ob.set_up()
Set my_list in base class to be [1,2....9]
Inherit Start() into Operation() and modify list [1,2....9] that I created in the object Process()
But things are not moving according to my understanding. my_list is empty as set_up is False for classes Process and Operation. How do I change my code to make it work according to what my understanding is?
Edit- In the base class, there are two methods, one has to run when the object is initialised, right at the beginning. It will set a flag to True. After which another method in the same base class needs to run according to that flag
What you’re doing here:
start_ob = Start()
start_ob.set_up()
process_ob = Process()
process_ob.check_flag()
op_ob = Operation()
op_ob.perform_op()
… is creating three entirely separate objects. Each one has its own my_list. Just like you can have three different int objects and they’re all separate values, you can have three different Start objects and they’re all separate values.
What you probably wanted is:
ob = Operation()
ob.set_up()
ob.check_flag()
ob.perform_op()
Now you have a single object, which is an Operation, and therefore a Process, and therefore a Start, so you can call methods from any of those three types and they will affect your object’s value. And now you’re using inheritance.

Trouble with specific class inheritance behaviour

So I am trying to get my data structure set up for an automated generator I am writing for a roleplaying game and I am having trouble with some specific inheritance quirks. Here is an excerpt of the data structure.
class data():
def __init__(self):
self.races = Races()
class Races(data):
def __init__(self):
self.humans = Humans()
class Humans(Races):
def __init__(self):
self.Characteristics = {
'Brawn':2,
'Agility':2,
'Intellect':2,
'Cunning':2,
'Willpower':2,
'Presence':2
}
There is a lot more in the structure but this is just a bottom to top overview. I also know it is indented weirdly but that is strictly stack overflow.
Now I wish to have two behaviors from this object.
The ability to call any characteristic with
data.races.humans.Characteristic['brawn']
as the calling format.
And too also be able to iterate through subclasses with a generator like:
(subclass for subclass in data.races.__subclasses__())
obviously after I have instantiated the object.
Now I have tried changing the structure several times and I can get it so EITHER I can call it with dot notation, but it returns AttributeError: 'Races' object has no attribute '__subclasses__'
Or vice versa by completely separating it into a more traditional structure but then I cannot call in dot notation and this makes it very hard to keep everything organized and readable.
Can anyone suggest what I am doing wrong or a more Pythonic way to approach the problem?
Let's start in the middle. Presumably, a character of any race has the same attributes, just different values for those attributes.
class Race:
def __init__(self):
self.life = 100 # 100% healthy
class Humanoid(Race):
def __init__(self):
super().__init__()
self.legs = 2
class Insectoid(Race):
def __init__(self):
super().__init__()
self.legs = 8
class Human(Humanoid):
def __init__(self):
super().__init__()
self.brawn = 2
self.agility = 2
self.intellect = 2
self.cunning = 2,
self.willpower = 2
self.presence = 2
class Elf(Humanoid):
def __init__(self):
super.__init__()
self.brawn = 1
self.agility = 3
self.intellect = 3
self.cunning = 2
self.willpower = 3
self.presence = 1
Now, any particular character would be instantiated as the correct class:
some_elf_1 = Elf()
some_human_1 = Human()
some_human_2 = Human()
for character in [some_elf_1, some_human_1, some_human_2]:
print("Brawn: ", character.brawn)
In the preceding, it doesn't matter what the actual type of each character is; as long as you know that it is some subclass of Race (or an instance of Race itself), it will have a brawn attribute that you can access.
You data class doesn't really seem necessary without more detail.
So, While the answer given put me on the right track I realized what I needed and am just throwing in my lot for any poor souls.
Firstly - I realized what was wrong with my generator, I was calling on the initialized object instead of the class object. Objects do not have a subclasses attrib and I was mis-informed by most of the guides I read!
Secondly, I considered using a metaclass to get the iterating behavior I wanted from my objects can simply be achieved with a registry attribute that is a dict of all the initialized subclasses.
lass Races(data):
def __init__(self):
self.humans = Humans()
self.droids = Droids()
self.twileks = Twileks()
self.registry = {
'humans':self.humans,
'droids':self.droids,
'twileks':self.twileks
}
This allows me to iterate through certain values for different races after they have been initialized.
Thanks for all the great answers!

How to initialise class attributes?

I currently have the following two ways:
class Venue:
store = Database.store()
ids = [vid for vid in store.find(Venue.id, Venue.type == "Y")]
def __init__(self):
self.a = 1
self.b = 2
OR
class Venue:
#classmethod
def set_venue_ids(cls):
store = Database.store()
cls.ids = [vid for vid in store.find(Venue.id, Venue.type == "Y")]
def __init__(self):
self.a = 1
self.b = 2
And before using/instantiating the class I would call:
Venue.set_venue_ids()
What would be the correct way of achieving this?
If it's the first way, what would I do if the instantiation of the attribute required more complex logic that could be done more simply through the use of a function?
Or is there an entirely different way to structure my code to accomplish what I'm trying to do?
From a purely technical POV, a class is an instance of its metaclass so the metaclass initializer is an obvious candidate for class attributes initialization (at least when you have anything a bit complex).
Now given the canonical lifetime of a class object (usually the whole process), I would definitly not use an attribute here - if anyone adds or removes venues from your database while your process is running, your ids attributes will get out of sync. Why don't you use a classmethod instead to make sure your data are always have up to date ?
Oh and yes, another way to construct your Venue.ids (or any other class attribute requiring non-trivial code) without having complex code at the class top-level polluthing the class namespace (did you noticed that in your first example store becomes a class attributes too, as well as vid if using Python 2.x ?) is to put the code in a plain function and call that function from within your class statement's body, ie:
def list_venue_ids():
store = Database.store()
# I assume `store.find()` returns some iterator (not a `list`)
# if it does return a list, you could just
# `return store.find(...)`.
return list(store.find(Venue.id, Venue.type == "Y"))
class Venue(object):
ids = list_venue_ids()
def __init__(self):
self.a = 1
self.b = 2

How to refer to a child class from a parent method?

In the following sample, is there a magic word I can put in place of <ChildClass> that works like the opposite of super?
class Parent(object):
def __init__(self):
print <ChildClass>.x
class someChild(Parent):
x = 10
It is a stupid example, but it shows my intention. By the way, using someChild will not work, because there are many child classes.
The only solution I can think of is to have a constructor in every child class that calls the constructor of Parent with a reference to itself (or even to pass x), but I would like to avoid having a constructor at all in each child.
What is wrong with just using self.x?
class Parent(object):
x = None # default value
def __init__(self):
print self.x
class someChild(Parent):
x = 10
def __init__(self):
Parent.__init__(self)
class otherChild(Parent):
x = 20
def __init__(self):
Parent.__init__(self)
a = someChild()
# output: 10
b = otherChild()
# output: 20
Note how this works even if Parent has a class attribute x as well (None in the above example)- the child's takes precedence.
self.x will work if the instance doesn't have an x attribute.
type(self).x if the instance has an x attribute and you want the class's value, essentially skipping over the instance.

Categories

Resources