I've created a base class and a subclass. I'll be creating more subclasses, however I have some general functions that will be used across all subclasses. Is this the proper way of setting it up? I'm assuming it would be easier to add the def to the base class and then call it within each subclass. Is that possible to do or recommended?
"""
Base class for all main class objects
"""
class Node(object):
def __init__(self, name, attributes, children):
self.name = name
self.attributes = attributes if attributes is not None else {}
self.children = children if children is not None else []
"""
contains the settings for cameras
"""
class Camera(Node):
def __init__(self, name="", attributes=None, children=None, enabled=True):
super(Camera, self).__init__(name=name, attributes=attributes, children=children)
self.enabled = enabled
# defaults
add_node_attributes( nodeObject=self)
# General class related functions
# ------------------------------------------------------------------------------
""" Adds attributes to the supplied nodeObject """
def add_node_attributes(nodeObject=None):
if nodeObject:
nodeObject.attributes.update( { "test" : 5 } )
# create test object
Camera()
You should add the general methods on the base class and call them from the subclass:
class Node(object):
def __init__(self, name, attributes, children):
self.name = name
self.attributes = attributes if attributes is not None else {}
self.children = children if children is not None else []
def add_node_attributes(self):
self.attributes.update( { "test" : 5 } )
This allows you to take maximum advantage of inheritance. Your subclasses will have the method add_node_attributes available to them:
c=Camera()
c.add_node_attributes()
You can also call it from within the child class:
class Camera(Node):
def __init__(self, name="", attributes=None, children=None, enabled=True):
super(Camera, self).__init__(name=name, attributes=attributes, children=children)
self.enabled = enabled
# defaults
self.add_node_attributes()
Related
I do this fairly frequently, and maybe it's bad design and there's a better way to do it, but I haven't ever had any issue.
When defining an object with a parent and assigning an attribute of that object to an attribute of the parent, which of these methods of writing is more "Pythonic"?
Assuming we are doing something like...
class SomeParent:
def __init__(self, value):
self.value = value
class SomeChild { ... }
parentObj = SomeParent(value="foo")
childObj = SomeChild(parent=parentObj)
Would the "proper" way to write the __init__ for SomeChild be...
class SomeChild:
def __init__(self, parent):
self.parent = parent
self.value = parent.value
Or...
class SomeChild:
def __init__(self, parent):
self.parent = parent
self.value = self.parent.value
The only difference being the use of self when defining value on the child object. Obviously, they both (seemingly) work exactly the same, does it even matter which is used? Or am I overthinking this?
You can do this more cleanly with a property:
class A:
def __init__(self, value):
self.value = value
class B:
def __init__(self, a):
self.a = a
#property
def value(self):
return self.a.value
assert B(A(2)).value == 2
This way, B.value will automatically update with a.value.
Note: I purposefully don't use the names "parent" and "child" which would imply inheritence. You are not using inheritence.
class Remote:
aa=7
def __init__(self):
self.name="Lenovo"
self.b=self.Battery()
print("this is outer",self.b.t)
class Battery:
def __init__(self):
self.name="Hp"
self.t="df"
self.c=self.Cover()
class Cover:
def __init__(self):
self.name="Arplastic"
c1=Remote()
I knew today about inner class but i don't know how to i access properties and methods of outer class into inner class please let me know anyone.
Change the constructor(s) of the inner class(es) to accept a parent argument and have the creating instance pass itself to it:
class Remote:
aa=7
def __init__(self):
self.name="Lenovo"
self.b=self.Battery(self)
print("this is outer",self.b.t)
class Battery:
def __init__(self,parent):
self.name="Hp"
self.t="df"
self.c=self.Cover(self)
self.parent=parent
class Cover:
def __init__(self,parent):
self.name="Arplastic"
self.parent=parent
c1=Remote()
print(c1.b.c.parent.parent.name) # prints 'Lenovo'
One approach is to make a metaclass that automatically creates self.parent attributes for nested classes. Note that there is a trade-off between readability and boilerplate here - many programmers would rather you just manually pass parents as arguments and add them to __init__ methods. This is more fun though, and there is something to be said for having less cluttered code.
Here is the code:
import inspect
def inner_class(cls):
cls.__is_inner_class__ = True
return cls
class NestedClass(type):
def __new__(metacls, name, bases, attrs, parent=None):
attrs = dict(attrs.items())
super_getattribute = attrs.get('__getattribute__', object.__getattribute__)
inner_class_cache = {}
def __getattribute__(self, attr):
val = super_getattribute(self, attr)
if inspect.isclass(val) and getattr(val, '__is_inner_class__', False):
if (self, val) not in inner_class_cache:
inner_class_cache[self, val] = NestedClass(val.__name__, val.__bases__, val.__dict__, parent=self)
return inner_class_cache[self, val]
else:
return val
attrs['__getattribute__'] = __getattribute__
attrs['parent'] = parent
return type(name, bases, attrs)
class Remote(metaclass=NestedClass):
aa = 7
def __init__(self):
self.name = "Lenovo"
self.b = self.Battery()
print("this is outer", self.b.t)
#inner_class
class Battery:
def __init__(self):
self.name = "Hp"
self.t = "df"
self.c = self.Cover()
#inner_class
class Cover:
def __init__(self):
self.name = "Arplastic"
print(f'{self.parent=}, {self.parent.parent=}')
c1 = Remote()
print(f'{c1.b.c.parent.parent is c1=}')
print(f'{isinstance(c1.b, c1.Battery)=}')
Output:
self.parent=<__main__.Battery object at 0x7f11e74936a0>, self.parent.parent=<__main__.Remote object at 0x7f11e7493730>
this is outer df
c1.b.c.parent.parent is c1=True
isinstance(c1.b, c1.Battery)=True
The way this works is by storing the parent as a class attribute (which is None by default), and replacing the __getattribute__ method so that all inner classes are replaced with NestedClasses with the parent attribute correctly filled in.
The inner_class decorator is used to mark a class as an inner class by setting the __is_inner_class__ attribute.
def inner_class(cls):
cls.__is_inner_class__ = True
return cls
This is not strictly necessary if all attributes that are classes should be treated as inner classes, but it's good practice to do something like this to prevent Bar.foo being treated as an inner class in this example:
class Foo:
pass
class Bar(metaclass=NestedClass):
foo = Foo
All the NestedClass metaclass does is take the description of the class and modify it, adding the parent attribute:
class NestedClass(type):
def __new__(metacls, name, bases, attrs, parent=None):
attrs = dict(attrs.items())
...
attrs['parent'] = parent
return type(name, bases, attrs)
...and modifying the __getattribute__ method. The __getattribute__ method is a special method that gets called every time an attribute is accessed. For example:
class Foo:
def __init__(self):
self.bar = "baz"
def __getattribute__(self, item):
return 1
foo = Foo()
# these assert statements pass because even though `foo.bar` is set to "baz" and `foo.remote` doesn't exist, accessing either of them is the same as calling `Foo.__getattribute(foo, ...)`
assert foo.bar == 1
assert foo.remote == 1
So, by modifying the __getattribute__ method, you can make accessing self.Battery return a class that has its parent attribute equal to self, and also make it into a nested class:
class NestedClass(type):
def __new__(metacls, name, bases, attrs, parent=None):
attrs = dict(attrs.items())
# get the previous __getattribute__ in case it was not the default one
super_getattribute = attrs.get('__getattribute__', object.__getattribute__)
inner_class_cache = {}
def __getattribute__(self, attr):
# get the attribute
val = super_getattribute(self, attr)
if inspect.isclass(val) and getattr(val, '__is_inner_class__', False):
# if it is an inner class, then make a new version of it using the NestedClass metaclass, setting the parent attribute
if (self, val) not in inner_class_cache:
inner_class_cache[self, val] = NestedClass(val.__name__, val.__bases__, val.__dict__, parent=self)
return inner_class_cache[self, val]
else:
return val
attrs['__getattribute__'] = __getattribute__
attrs['parent'] = parent
return type(name, bases, attrs)
Note that a cache is used to ensure that self.Battery will always return the same object every time rather than re-making the class every time it is called. This ensures that checks like isinstance(c1.b, c1.Battery) work correctly, since otherwise c1.Battery would return a different object to the one used to create c1.b, causing this to return False, when it should return True.
And that's it! You can now enjoy nested classes without boilerplate!
I would like to overwrite an inherited method in a class (see below example for __init__ method) while letting its children still use the Parents version.
I know that I could achieve the desired behaviour redefining the __init__ method in the GrandChild class or using multiple inheritance. However my question aims at a way to achieve the same with changes only to the Child class and its __init__ implementation.
(The actual use case is significantly more complex 'legacy code' with several classes on each level. The motivation of this question is therefore to achieve the desired behaviour in the respective class without having to touch the implementation of the other classes or the inheritance structure)
If this is impossible I would also appreciate an explanation to that effect.
class Parent:
def __init__(self, a,b):
self.a = a
self.b = b
def __str__(self):
return f"{self.a}, {self.b}"
class Child(Parent):
# I would like to overwrite this method only for the Child Class and none of its children / downstream inhertiances
def __init__(self, a):
super().__init__(a, None)
class GrandChild(Child):
# This Class should use the __init__ method of class Parent
pass
parent = Parent("a","b")
child = Child("c")
# This throws a Type error right now since it calls the init method of class Child
grandchild = GrandChild("d", "e")
EDIT:
As mentioned above I am aware that I can achieve the desired behaviour in different ways such as changing the class structure (as below). However the question is really more about wether python allows doing it with changes only to the Child class. If this is actually impossible (not merely undesirable) in python, an explanation why would do more to answer my question than providing alternative implementations that change anything beyond the implementation of the Child class.
class ChildCommonFunctionality(Parent):
# Use this class for all common functionality originally provided by Child Class
pass
class Child(ChildCommonFunctionality):
# Use this class to override the init method
def __init__(self, a):
super().__init__(a, None)
class GrandChild(ChildCommonFunctionality):
# This Class should use the __init__ method of class Parent
pass
I have found a way using _init_subclass to make sure that all subclasses of Child use the constructor of Parent instead of the one defined in Child inspired by this post:
class Parent:
def __init__(self, a,b):
self.a = a
self.b = b
def __str__(self):
return f"{self.a}, {self.b}"
class Child(Parent):
# I would like to overwrite this method only for the Child Class and none of its children / downstream inhertiances
def __init__(self, a):
super().__init__(a, None)
def __init_subclass__(cls):
cls.__init__ = super().__init__
class GrandChild(Child):
# This Class should use the __init__ method of class Parent
pass
Even though this is a bit hacky it provides the desired functionality of actually bypassing Childs init method
You could do :
class Parent:
def __init__(self, a, b = None):
self.a = a
self.b = b
def __str__(self):
return f"{self.a}, {self.b}"
class Child(Parent):
# I would like to overwrite this method only for the Child Class and none of its children / downstream inhertiances
def __init__(self, a, b = None):
super().__init__(a, b) # Or None instead of b... but that's not good when called by GrandChild
class GrandChild(Child):
# This Class should use the __init__ method of class Parent
pass
parent = Parent("a","b")
child = Child("c")
grandchild = GrandChild("d", "e")
EDIT : you could also replace the optional parameter by a mandatory one in GrandChild :
class GrandChild(Child):
def __init__(self, a, b):
super().__init__(a, b)
This code might do the trick, adding a few lines to the suggestion of #dspr:
class Parent:
def __init__(self, a, b = None):
self.a = a
self.b = b
def __str__(self):
return f"{self.a}, {self.b}"
class Child(Parent):
# I would like to overwrite this method only for the Child Class and none of its children / downstream inhertiances
def __init__(self, a, b = None):
if type(self) == Child:
if b is not None:
raise ValueError(
"Second argument is not allowed for direct use in Child class")
super().__init__(a, None) #Or (a, b) if you trust b to be None as it is here
else:
super().__init__(a, b)
class GrandChild(Child):
# This Class should use the __init__ method of class Parent
pass
parent = Parent("a","b")
child = Child("c")
print(child.b) # None
grandchild = GrandChild("d", "e")
print(grandchild.b) # e
child = Child("f", "g")
print(child.b) # ValueError
Consider the following python code:
class Parent(object):
def __init__(self, name, serial_number):
self.name = name
self.serial_number = serial_number
class ChildA(Parent):
def __init__(self, name, serial_number):
self.name = name
self.serial_number = serial_number
super(ChildA, self).__init__(name = self.name, serial_number = self.serial_number)
def speak(self):
print("I am from Child A")
class ChildB(Parent):
def __init__(self, name, serial_number):
self.name = name
self.serial_number = serial_number
super(ChildB, self).__init__(name = self.name, serial_number = self.serial_number)
def speak(self):
print("I am from Child B")
class GrandChild(ChildA, ChildB):
def __init__(self, a_name, b_name, a_serial_number, b_serial_number):
self.a_name = a_name
self.b_name = b_name
self.a_serial_number = a_serial_number
self.b_serial_number = b_serial_number
super(GrandChild, self).__init_( something )
When running the super function in GrandChild, what is the proper way to format the __init__ arguments so that ChildA and ChildB both get the correct arguments?
Also how do you access the two different versions of the speak method (ChildA's version and ChildB's version) from within the GrandChild class?
so, when you call super from the grandchild, ChildA's __init__ method will be called because super follows the __mro__ property (parents left to right then grandparents left-to-right, then great grandparents, ...)
Since ChildA's init also calls super, then all the super calls will be chained, calling child b's __init__ and eventually the parent init.
For that to work, your interface generally needs to be consistent. That is positional arguments need to mean the same things, and be in the order.
In situations where that's not the case, keyword arguments may work better.
class Parent:
def __init__(self, name, serial, **kwargs):
self.name = name
self.serial = serial
class ChildA(Parent):
def __init__(self, a_name, a_serial, **kwargs):
self.a_name = a_name
self.a_serial = a_serial
super().__init__(**kwargs)
class ChildB(Parent):
def __init__(self, b_name, b_serial, **kwargs):
self.b_name = b_name
self.b_serial = b_serial
super().__init__(**kwargs)
class GrandChild(ChildA, ChildB):
def __init__(self):
super().__init__(name = "blah", a_name = "a blah", b_name = "b blah", a_serial = 99, b_serial = 99, serial = 30)
Also note that in your code name and serial are reused as instance properties between all the classes and that's probably not what you want.
In python, you can explicitly call a particular method on (one of) your parent class(es):
ChildA.__init__(self, a_name, a_serial)
ChildB.__init__(self, b_name, b_serial)
Note that you need to put the self in explicitly when calling this way.
You can also – as you did – use the super() way, which will call the "first" parent. The exact order is dynamic, but by default it will do left-to-right, depth-first, pre-order scans of your inheritance hierarchy. Hence, your super() call will only call __init__ on ChildA.
I often find I have class instances that are descendants of other class instances, in a tree like fashion. For example say I'm making a CMS platform in Python. I might have a Realm, and under that a Blog, and under that a Post. Each constructor takes it's parent as the first parameter so it knows what it belongs to. It might look like this:
class Realm(object):
def __init__(self, username, password)
class Blog(object):
def __init__(self, realm, name)
class Post(object);
def __init__(self, blog, title, body)
I typically add a create method to the parent class, so the linkage is a bit more automatic. My Realm class might look like this:
class Realm(object):
def __init__(self, username, password):
...
def createBlog(self, name):
return Blog(self, name)
That allows the user of the API to not import every single module, just the top level one. It might be like:
realm = Realm("admin", "FDS$#%")
blog = realm.createBlog("Kittens!")
post = blog.createPost("Cute kitten", "Some HTML blah blah")
The problem is those create methods are redundant and I have to pydoc the same parameters in two places.
I wonder if there's a pattern (perhaps using metaclasses) for linking one class instance to a parent class instance. Some way I could call code like this and have the blog know what it's parent realm is:
realm = Realm("admin", "FDS$#%")
blog = realm.Blog("Kittens!")
You could use a common base class for the containers featuring an add() method
class Container(object):
def __init__(self, parent=None):
self.children = []
self.parent = parent
def add(self, child)
child.parent = self
self.children.append(child)
return child
and make the parent parameter optional in the derived classes
class Blog(Container):
def __init__(self, name, realm=None):
Container.__init__(realm)
self.name = name
Your code above would now read
realm = Realm("admin", "FDS$#%")
blog = realm.add(Blog("Kittens!"))
post = blog.add(Post("Cute kitten", "Some HTML blah blah"))
You wouldn't have any create...() methods any more, so no need to document anything twice.
If setting the parent involves more than just modifying the parent attribute, you could use a property or a setter method.
EDIT: As you pointed out in the comments below, the children should be tied to the parents by the end of the contstructor. The above approach can be modified to support this:
class Container(object):
def __init__(self, parent=None):
self.children = []
self.parent = None
def add(self, cls, *args)
child = cls(self, *args)
self.children.append(child)
return child
class Realm(Container):
def __init__(self, username, password):
...
class Blog(Container):
def __init__(self, realm, name):
...
class Post(Container):
def __init__(self, blog, title, body):
...
realm = Realm("admin", "FDS$#%")
blog = realm.add(Blog, "Kittens!")
post = blog.add(Post, "Cute kitten", "Some HTML blah blah")
What about something like this, just subclassing it. In my Realm constructor:
class Realm(object):
def __init__(self, username, password):
...
parent = self
original_constructor = blog.Blog.__init__
class ScopedBlog(blog.Blog):
def __init__(self, *args):
self.parent = parent
original_constructor(self, *args)
self.Blog = ScopedBlog
Seems to work. And it could be generalized with base classes or meta classes.