I have a PlayoffCreator class to create playoff matches. This class has a Bracket instance which generates the structure of the bracket, and each match in this structure is a Node, made up of two instances of the class Element.
Then the PlayoffCreator goes through each Node the Bracket has and processes it, creating the match and doing other necessary operations. When processing a Node, both Elements are processed: this Element class has multiple subclasses and each define a different process() behavior.
The problem is a Node is instantiated by passing two Elements:
class Node:
def __init__(self, bracket, home_element, away_element):
pass
But an Element is also instantiated by passing a Node, because the process() method acts on data found both in Bracket and PlayoffCreator, both accessible through the Node.
class Element:
def __init__(self, node):
pass
How does one usually deal with this circular dependency on instantiation issue?
I see two possibilities.
1 - Instantiate the two elements, then setup the dependency
class A:
def __init__(self):
self.b = None
class B:
def __init__(self):
self.a = None
a = A()
b = B()
a.b = b
b.a = a
You can push this further by adding a b optional parameter in A's constructor, that defaults to None, and doing the same for B:
class A:
def __init__(self, b=None):
self.b = b
class B:
def __init__(self, a=None):
self.a = a
This allows you to instantiate the second by passing the first's instance:
a = A()
b = B(a)
a.b = b
2 - Instantiate B at A instantiation, then get that instance
class A:
def __init__(self):
self.b = B(self)
class B:
def __init__(self, a):
self.a = a
a = A()
b = a.b
Both method have their advantages and drawbacks.
I would prefer n°1, for it's more flexible, because symmetrical.
However, if there is a logical hierarchy between the two classes, n°2 might be used as well.
Related
Suppose we have 3 classes, 2 parent and 1 child that inherits from both parents:
import logging
class A:
def __init__(self):
self.parent_logger = logging.getLogger("parent")
class B:
def __init__(self):
self.parent_logger = logging.getLogger("parent")
class C(A, B):
def __init__(self):
A.__init__(self)
B.__init__(self)
test_class = C()
test_class.parent_logger.info("sample")
Which class does the parent_logger get used from when instantiating class C and calling parent logger on it?
The order is left to right, so in this case the logger gets used from class A, because you declared C as inheriting from (A, B). First A is searched for the logger, then all its parent classes (if it has any) and then B. If you defined C as class C(B, A): then B would be searched first and the logger from B would be used.
https://www.educative.io/answers/what-is-mro-in-python
I'm coding in Python. I have a Base class which contains several methods. There are some child classes; they may have their own methods. I want the Base class constructor to create the object not of the class itself but the object of one of the child classes depending on the argument.
For example, guess our child classes are Point, Line, and Plane, all are inherited from Base, and the difference between them is set by the dim attribute of the Base class.
class Base():
def __init__(self, dim, a, b):
self.dim = dim
self.a = a
self.b = b
class Point(Base):
def __init__(self, a, b):
super().__init__(1, a, b)
class Line(Base):
def __init__(self, a, b):
super().__init__(2, a, b)
class Plane(Base):
def __init__(self, a, b):
super().__init__(3, a, b)
If I explicitly create a Point, the object type will be Point:
pointA = Point(0, 0)
type(pointA) # __main__.Point
But if I do the same through the Base constructor, the object will be of class Base:
pointB = Base(1, 0, 0)
type(pointB) # __main__.Base
So I'd like to change this behavior and make the Base constructor return a Point, Line or Plane object if the dim attribute is equal to 1, 2 or 3 respectively. How can I do this?
EDIT:
Based on this thread (Improper use of __new__ to generate class instances?) I overrid the Base.__new__() and got the following code:
class Base():
def __new__(cls, a, b, dim):
if dim == 1:
return object.__new__(Point)
elif dim == 2:
return object.__new__(Line)
elif dim == 3:
return object.__new__(Plane)
class Point(Base):
def __init__(self, a, b, dim):
self.a = a
self.b = b
class Line(Base):
def __init__(self, a, b, dim):
self.a = a
self.b = b
class Plane(Base):
def __init__(self, a, b, dim):
self.a = a
self.b = b
The code above works, but it requires an explicit setting of the dim argument even when I create a new Point instance. A non-identical set of arguments in Base.__new__() and Point.__init__() raises an error. How can I keep this behavior but remove dim from the Point constructor?
You don't typically want to do this by instantiating a base case. While I suppose you could override __new__, I wouldn't advise it for this. Notably, though, __init__ has a None return type. So, regardless, you can't do this in __init__ - the object is already created at that point.
Instead, what you probably want is a static so-called 'Factory' method on your base class:
from typing import Type
class Base():
#staticmethod
def create(dim, a, b) -> Type[Base]:
# Decide which subclass you want to create, instantiate and return it.
...
new_obj = Base.create(x, y, z)
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
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I currently have some class foo() with some variables that are not only shared among all instances of the foo class, but also by other classes bar.
i.e.
class foo():
__init__(self, a, b):
self.a = a
self.b = b
class bar():
__init__(self, a, b):
self.a = a
self.b = b
One solution would be to make a and b class variables, but how do I do that cleanly during construction? Could I just put both classes in the same file and have them reference some global variables a and b? Is that bad practice?
Since you did not provide your intention or real-world situation, I'll just provide some ways of sharing variable access.
1st option: global.
a=b=None
class foo():
def __init__(self, _a, _b):
global a, b
a, b = _a, _b
class bar():
def __init__(self, _a, _b):
global a, b
a, b = _a, _b
2nd option: foo's class vars
class foo():
a = b = None
def __init__(self, a, b):
foo.a, foo.b = a, b
class bar():
def __init__(self, a, b):
foo.a, foo.b = a, b
3rd option: inheritance
class foo():
def __init__(self, a, b):
self.a, self.b = a, b
class bar(foo):
pass
4th option: outer class
class outer():
a = b = None
class foo():
def __init__(self, a, b):
outer.a, outer.b = a, b
class bar():
def __init__(self, a, b):
outer.a, outer.b = a, b
5th option: compsition
class foo():
def __init__(self, a, b):
self.a, self.b = a, b
class bar():
def __init__(self, a, b):
self.foo = foo(a,b)
6th option: closure over outer-function local variables
def outer():
a = b = None
class foo():
def __init__(self, _a, _b):
nonlocal a, b
a, b = _a, _b
class bar():
def __init__(self, _a, _b):
nonlocal a, b
a, b = _a, _b
... #things with foo and bar
7th option: closure over foo's __init__ local variables.
class foo():
def __init__(self, a, b):
self.a, self.b = a, b
class bar():
nonlocal a, b
#do things with a and b directly
self.bar = bar()
You could do this:
class Foo(object):
def __init__(self, a, b):
self.a = a
self.b = b
class Bar(Foo):
pass
By inheriting from Foo, you'll be adopting Foo's construction method as well so it will act the same way. If you need to override it, you can set it up this way in Bar:
def __init__(self, a, b, c):
super(Bar, self).__init__(a, b)
self.c = c
super will call your base class' method first (in this case, Foo) and then allow you to add on if you'd like. Here's the documentation on super, if you're interested.
The usual solution is to make an object that stores the shared information, then pass that when instantiating the classes that need it. Often this is some kind of configuration information, so we'll call the class Config:
class Config(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
# default values
number = 0
text = "Nothing"
Since Python is duck-typed, any object can be used to hold this configuration; it can be an instance of a class or the class itself. The former is handy when the data is specified at runtime. The latter can be convenient since it allows the programmer to define the various bundles of attributes using inheritance at coding time. The Config class here lets you have it either way: you can instantiate it, passing keyword arguments with the shared values, or you can subclass it, providing the shared values as class attributes.
In your Foo and Bar classes you then just accept the shared data in the constructor:
# these classes both need certain pieces of data
# but are not related by inheritance
class Foo(object):
def __init__(self, shared):
self.shared = shared
class Bar(object):
def __init__(self, config):
self.config = config
And then you can either instantiate the Config class, or define a subclass, and pass the resulting object to the new objects:
# use an instance
adams_config = Config(text="Don't Panic", number=42)
foo1, bar1 = Foo(adams_config), Bar(adams_config)
# use a subclass
class LincolnConfig(Config):
number = 87
text = "Four score and seven years ago"
foo2, bar2 = Foo(LincolnConfig), Bar(LincolnConfig)
Now methods of your Foo and Bar class can get self.shared.number or self.config.text (and so on) to access the data.
Since the instances of your various classes are all holding references to the same object, a change to e.g. adams_config or LincolnConfig would be seen by any instance of any class that holds a reference to one of these objects. If this isn't the behavior you want, you could fish the data you want to "freeze" out of the config object at instantiation and set it as attributes of your instance.
You could also just use a dictionary for data you want to access in various places, but I think the benefits of inheritance and attribute-access syntax are a good argument for doing it with classes.
You could even have a global configuration object that is used as a default value so you don't need to explicitly specify it if you want things to "just work." Here we'll just use the Config class itself for that, since it already has default values for the attributes we're interested in:
class Baz(object):
def __init__(self, config=Config):
self.config = config
By using this approach instead of global variables, you make it easier for clients using your objects to have numerous instances with different settings, rather than being limited to one "bundle" of settings for all instances.
I'm not sure what you mean by "cleanly during construction"
You can use class variables by defining them outside the function like:
class A:
x = 1
def __init__(self):
pass
And just call A.x whenever you need the variable, within other classes or wherever
Assume that we have an object k of type class A. We defined a second class B(A). What is the best practice to "convert" object k to class B and preserve all data in k?
This does the "class conversion" but it is subject to collateral damage. Creating another object and replacing its __dict__ as BrainCore posted would be safer - but this code does what you asked, with no new object being created.
class A(object):
pass
class B(A):
def __add__(self, other):
return self.value + other
a = A()
a.value = 5
a.__class__ = B
print a + 10
a = A() # parent class
b = B() # subclass
b.value = 3 # random setting of values
a.__dict__ = b.__dict__ # give object a b's values
# now proceed to use object a
Would this satisfy your use case? Note: Only the instance variables of b will be accessible from object a, not class B's class variables. Also, modifying variables in a will modify the variable in b, unless you do a deepcopy:
import copy
a.__dict__ = copy.deepcopy(b.__dict__)
class A:
def __init__(self, a, b):
self.a = a
self.b = b
class B(A):
def __init__(self, parent_instance, c):
# initiate the parent class with all the arguments coming from
# parent class __dict__
super().__init__(*tuple(parent_instance.__dict__.values()))
self.c = c
a_instance = A(1, 2)
b_instance = B(a_instance, 7)
print(b_instance.a + b_instance.b + b_instance.c)
>> 10
Or you could have a sperate function for this:
def class_converter(convert_to, parent_instance):
return convert_to(*tuple(parent_instance.__dict__.values()))
class B(A):
def __init__(self, *args):
super().__init__(*args)
self.c = 5
But using the 2nd method, I wasn't able to figure out how to pass additional values