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

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

Related

Instantiate an child object directly from a parent object

I have two objects one inherits from the other and the only difference between them is a few attribute fields:
class Parent:
def __init__(self,a, b):
self.a = a
self.b = b
def methodA(self):
# do something
pass
class Child(Parent):
def __init__(self,c,**kwargs):
self.c = c
super().__init__(**kwargs)
I have an instance of the parent object and I want to find a fast way in python to create an instance of the child object which only has one additional field by using the already existing parent object.
Is there a python way or module that lets you do that easily. IN my real code the parent class has hundreds of fields and it is a bit inefficient to just reassign its value.
The canonical solution is to add a class method to Child that works as a constructor. It takes a Parent instance and returns the Child instance with the proper attributes.
For example:
class Parent:
def __init__(self,a, b):
self.a = a
self.b = b
class Child(Parent):
def __init__(self,c,**kwargs):
self.c = c
super().__init__(**kwargs)
#classmethod
def from_parent(cls, parent, c):
return cls(a=parent.a, b=parent.b, c=c)
p = Parent(a=1, b=2)
c = Child.from_parent(parent=p, c=3)
print(c.a, c.b, c.c) # output: 1 2 3
I would argue that your Parent class having hundreds of attributes is irrelevant to the answer. Yes, it's tedious having to explicitly write every attribute of the Parent instance in the from_parent method, but that's simply a limitation of having a class with that many attributes anyway. Possibly, a better design choice would be to encapsulate groups of Parent attributes into proper classes, so that only those instances need to be delivered to the Child class upon initialization.
Ok the other suggestions for making a method that takes in parent attributes and creates a child object is ok but adds unnecessary code I think. I made this solution, which I ended up using. It doesnt accept the parent object directly in as an argument but it is more concise I think:
class Parent:
def __init__(self, a, b):
self.a = a
self.b = b
class Child(Parent):
def __init__(self,c, **kwargs):
self.c = c
super().__init__(**kwargs)
# So if I start with this parent object
parent_args = {"a":23,"b":"iuhsdg"}
parent =Parent(**parent_args)
# I then make child with all the parent attributes plus some more
child_args = {"c":567}
child_args.update(vars(parent))
child = Child(**child_args)

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

Composite and Hierarchy without instance shared variables

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.

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