Access Child class variables in Parent class - python

I have a Parent class and a inherited child class, I would like to know how to access the child class variable in my Parent class..
I tried this and it fails -
class Parent(object):
def __init__(self):
print x
class Child(Parent):
x = 1;
x = Child();
Error:-
NameError: global name 'x' is not defined
This question is in relation to Django forms where we inherit the form class
and declare some class variables.
For example:-
My form looks like this
from django import forms
class EmployeeForm(forms.Form):
fname = forms.CharField(max_length=100)
lname = forms.CharField(max_length=100)
I believe the form fields are considered as class variable and somehow passed to the parent class..

Django does this with metaclasses. (Relevant Django source)
Here's a distilled example of the relevant code:
class Field(object):
def __init__(self, *args):
self.args = args
def __repr__(self):
return "Form(%s)" % (', '.join(map(repr, self.args)),)
class Meta(type):
def __new__(mcs, name, bases, attrs):
field_list = []
for k,v in attrs.items():
if isinstance(v, Field):
field_list.append(v)
cls = type.__new__(mcs, name, bases, attrs)
cls.fields = field_list
return cls
class Form(object):
__metaclass__ = Meta
class MyForm(Form):
fe1 = Field("Field1", "Vars1")
fe2 = Field("Field2", "Vars2")
x = "This won't appear"
form_fields = MyForm.fields
print(form_fields)
There are many questions on here about Python metaclasses (example), so I won't try to re-explain the concept.
In this case, when you create the class MyForm, each of the class attributes are checked for being instances of Field. If they are, they're added to a list (field_list).
The class is created, then an attribute .fields is added to the class, which is field_list, the list of Field elements.
You can then access the form fields through <FormSubclass>.fields or in the case of this example, MyForm.fields.
Edit:
It's worth noting that you can accomplish very similar functionality, without the metaclass syntactic sugar with something like:
class Field(object):
def __init__(self, *args):
self.args = args
def __repr__(self):
return "Form(%s)" % (', '.join(map(repr, self.args)),)
class Form(object):
def __init__(self):
self._fields = None
def fields(self):
if self._fields is None:
field_list = []
for k in dir(self):
v = getattr(self, k)
if isinstance(v, Field):
field_list.append(v)
self._fields = field_list
return self._fields
class MyForm(Form):
def __init__(self):
Form.__init__(self)
self.fe1 = Field("Field1", "Vars1")
self.fe2 = Field("Field2", "Vars2")
self.x = "This won't appear"
form_fields = MyForm().fields()
print(form_fields) # [Form('Field1', 'Vars1'), Form('Field2', 'Vars2')]

Short answer : you dont access subclasse's attributes from a parent class - because the parent class can not know what attributes a child class might have.
Long answer : ... unless the parent class defines a protocol allowing subclasses to let the parent class knows about at least part of it's own attributes.
Django's form framework (as well as django's orm FWIW) use such a protocol: the base Form class has a custom metaclass that collects the form.fields declared in a subclass - and do quite some black magic. FWIW, Django is oss so you could have answered the question yourself just reading the source code: https://github.com/django/django/blob/master/django/forms/forms.py

You need to refer to self.x to access Child class variables:
class Parent(object):
def __init__(self):
print(self.x)
class Child(Parent):
x = 1
if __name__ == '__main__':
child_instance = Child()

This might not help you in regards to Django Forms, but another alternative is to work with abstract classes. You would exchange attributes with methods/properties. It also prevents you from using the parent class by itself.
from abc import ABC, abstractmethod
class Parent(ABC):
#property
#abstractmethod
def x(self):
pass
def __init__(self):
print(self.x)
class Child(Parent):
#property
def x(self):
return 1
if __name__ == '__main__':
child_instance = Child() # prints "1"
parent_instance = Parent() # fails

Well, if I got you right... Maybe you're thinking of getting a field from the child class to work on the parent class. Well, that's polymorphism and it's done by overriding the parent class.
Let's assume you have :
A parent has x, now to increase x from the child and make it reflect in the parent, check the code below to get it.
class Parent:
def __init__(self, x):
self.x = x
def Print(self):
print(f"{self.x}")
class Child(Parent):
def __init__(self, x):
Parent.__init__(self, x)
x += 1
self.x = x
""""""
c1 = Child(2)
c1.Print()
#output: 3
""""""
c2 = Child(8)
c2.Print()
#output: 9

Related

Create child class object using parent class instance

lets say we have class A and it has one instance - x. How to make a child class of class A where I would be able to pass x as an argument and get all its parameters and pass it to child class object. precisely speaking I want to do something like this.
class A:
def __init__(self, parameter1, parameter2):
self.parameter1 = parameter1
self.parameter2 = parameter2
class B(A):
def __init__(self, Ainstance, someParameter):
super().__init__(**Ainstance.__dict__)
self.someParameter = someParameter
x = A(parameter1='1', parameter2='2')
x = B(x, someParameter='3')
print(x.parameter1)
print(x.parameter2)
print(x.someParameter)
the goal is to create a class where I would be able to get all the parameters of parent class object, and add my own attributes. The problem in the code above is I won't be able to do that with all classes because not all of them has __dict__ attribute.
I have this example code which I use to remind myself how to construct a proxy.
#soProxyPattern
class Example:
def __init__(self):
self.tag_name = 'name'
def foo(self):
return 'foo'
def bar(self, param):
return param
class Container:
def __init__(self, contained):
self.contained = contained
self.user_name = 'username'
def zoo(self):
return 0
def __getattr__(self, item):
if hasattr(self.contained, item):
return getattr(self.contained,item)
#raise item
c = Container(Example())
print(c.zoo())
print(c.foo())
print(c.bar('BAR'))
print(c.tag_name)
print(c.user_name)
The output is:
0
foo
BAR
name
username
This shows that Container can have its own attributes (methods or variables) which you can access over and above all of the attributes of the contained instance.
Instead of dict you could use the dir and getattr like this:
class A:
def __init__(self, parameter1, parameter2):
self.parameter1 = parameter1
self.parameter2 = parameter2
class B(A):
def __init__(self, Ainstance, someParameter):
parameters = {param: getattr(Ainstance, param) for param in dir(Ainstance) if not param.startswith("__")}
super().__init__(**parameters)
self.someParameter = someParameter
For a more detailed explanation see: Get all object attributes in Python?

How to inherit from pre-existing class instance in Python?

I have a class Parent:
class Parent:
def __init__(self, foo):
self.foo = foo
I then have another class Child which extends Parent. But I want Child to take a pre-existing instance of parent and use this as the parent to inherit from (instead of creating a new instance of Parent with the same constructor parameters).
class Child(Parent):
def __init__(self, parent_instance):
""" Do something with parent_instance to set this as the parent instance """
def get_foo(self):
return self.foo
Then I would ideally be able to do:
p = Parent("bar")
c = Child(p)
print(c.get_foo()) # prints "bar"
You could copy the content of the parents's __dict__ to the child's. You can use vars() builtin function to do so, and the dictionary's update() method.
class Child(Parent):
def __init__(self, parent_instance):
vars(self).update(vars(parent_instance))
def get_foo(self):
return self.foo
p = Parent("bar")
c = Child(p)
print(c.get_foo())
# prints "bar"
You can use your own constructor - provide a classmethod that takes an instance of a parent.
class Parent:
def __init__(self, foo):
self.foo = foo
class Child(Parent):
def get_foo(self):
return self.foo
#classmethod
def from_parent(cls, parent_instance):
return cls(parent_instance.foo)
p = Parent('bar')
c = Child.from_parent(p)
c.get_foo()
I'm not sure inheritance is the right solution here as it breaks the LSP in the __init__ method.
Maybe parents and children just share a common interface.
I'd prefer something like (python3.8):
from typing import Protocol
class FoeAware(Protocol):
#property
def foe(self):
...
class Parent:
def __init__(self, foe):
self._foe = foe
#property
def foe(self):
return self._foe
class Child:
def __init__(self, parent: FoeAware):
self.parent = parent
#property
def foe(self):
return self.parent.foe
p = Parent("bar")
c = Child(p)
c.foe # bar
The key point is that it takes advantage of polymorphism with a common interface FoeAware, which is preferable to an inheritance tree.
Using getattr() to fetch the attribute from the parent instance
class Parent:
def __init__(self, foo):
self.foo = foo
class Child(Parent):
def __init__(self, parent_instance):
self.parent_instance = parent_instance
def get_foo(self):
return self.foo
def __getattr__(self, attr):
return getattr(self.parent_instance, attr)
par = Parent("bar")
ch = Child(par)
print(ch.get_foo())
#prints bar

There are class methods. Can I have class attributes?

Consider the code snippet for the base class Base and its two child classes ChildA and ChildB:
class Base():
map = {}
def __init__(self, x):
type(self).map[x] = self
#classmethod
def get_state(cls, x):
if x in cls.map:
return cls.map[x]
return None
class ChildA(Base):
pass
class ChildB(Base):
pass
When creating an instance a = ChildA('foo') and calling ChildB.get_state('foo') after this, it returns the instance of ChildA.
However, expected (or rather the wishful behaviour) was None. The map attribute seems to be attached to the parent class Base, and the child classes share it.
Of course I could simply add a specific attribute to the child classes
class ChildA(Base):
map = {}
class ChildB(Base):
map = {}
but this is verbose if you have many child classes and several of those attributes. Is there any way to solve this by inheritance from Base?
You can use the init subclass hook:
class Base:
def __init_subclass__(cls, **kwargs):
cls.map = {}
Demo:
>>> class ChildA(Base):
... ...
...
>>> class ChildB(Base):
... ...
...
>>> ChildA.map == ChildB.map == {}
True
>>> ChildA.map is ChildB.map
False

How to pass all class variables from a parent *instance* to a child class?

Here's an example of what I'm trying to do:
class Parent():
def __init__():
self.parent_var = 'ABCD'
x = Child(self) # self would be passing this parent instance
class Child():
def __init__(<some code to pass parent>):
print(self.parent_var)
foo = Parent()
Now I know what you're thinking, why not just pass parent_var itself to the child instance? Well my actual implementation has over 20 class variables in Parent. I don't want to have to manually pass each variable to the __init__ of the Child instance that's instantiated in Parent-- is there a way to make all Parent class variables available to Child?
EDIT - SOLVED:
This is the way I found that works:
class Parent():
def __init__(self):
self.parent_var = 'ABCD' # but there are 20+ class vars in this class, not just one
x = Child(self) # pass this parent instance to child
class Child():
def __init__(self, parent):
for key, val in vars(parent).items():
setattr(self, key, val)
print(self.parent_var) # successfully prints ABCD
foo = Parent()
If you inherit from the parent class all variables will be present in child classes. Use super init in the child to make sure the parent class instantiates.
class Parent:
def __init__(self):
self.parent_var = 'ABCD'
class Child(Parent):
def __init__(self):
super().__init__()
child = Child()
print(child.parent_var)
prints:
'ABCD'
You would pass the instance of Parent like you would any value.
class Parent:
def __init__(self):
self.parent_var = 'ABCD'
x = Child(self)
class Child:
def __init__(self, obj):
print(obj.parent_var)
Found a solution and wanted to post the answer in case anyone who finds this needs it:
class Parent():
def __init__(self):
self.parent_var = "ABCD" # just an example
x = Child(self) # pass this parent instance (this object) to child
class Child():
def __init__(self, parent):
# copies variables from passed-in object to this object
for key, val in vars(parent).items():
setattr(self, key, val)
print(self.parent_var) # successfully prints ABCD
foo = Parent()

python: set read-only attribute for class objects

I've created a class object called 'Node'. I then created two subclasses 'Beetle' and 'Dakota'. You'll notice there is an attr called 'superclass' which is part of the baseclass. I want this attribute to be set for each subclass and once it's set it should never be changed. It's a read-only attribute. I was wondering how to set this attribute up correctly in order to be a read only attribute?
class Node(object):
def __init__(self, name, superclass, attributes, children):
self.name = name
self.superclass = superclass
self.attributes = attributes if attributes is not None else {}
self.children = children if children is not None else []
class Beetle(Node):
def __init__(self, name="", superclass="Cars", attributes=None, children=None, enabled=True):
super(Beetle, self).__init__(name=name, superclass=superclass, attributes=attributes, children=children)
self.enabled = enabled
class Dakota(Node):
def __init__(self, name="", superclass="Trucks", attributes=None, children=None, enabled=True):
super(Dakota, self).__init__(name=name, superclass=superclass, attributes=attributes, children=children)
self.enabled = enabled
Rename you variable to add __ to the begining
self.__superclass = superclass
you can't access self.__superclass with something like Dakota().__superclass
if you want to get value of __superclass add a function in Node class to return it.
def getsuperclass(self):
return self.__superclass
...
Dakota().getsuperclass()
To have a 'read-only' property in a class you can make use of the #property decoration.
An example:
class Dakota(object):
def __init__(self, superclass):
self.__superclass = superclass
#property
def superclass(self):
return self.__superclass
Usage:
>>> a = Dakota('lol')
>>> a.superclass
'lol'
>>> a.superclass = 'hah'
AttributeError...
AttributeError: can't set attribute
You can refer to this other answer thread

Categories

Resources