Encapsulate the decision which child class to initialize - python

I have a parent class and different child classes. I want to encapsulate the decision which child class is to initialize in the initialization.
A simple example:
class Person:
def __init__(self, name):
if self.name_is_male(name):
real_instance = Male(name)
else:
real_instance = Female(name)
return real_instance
def name_is_male(self, name):
if name == 'Donald':
return True
elif name == 'Daisy':
return False
else:
raise ValueError('unknown name!')
class Male(Person):
def __init__(self, name):
...
class Female(Person):
def __init__(self, name):
...
This simple example will end in a recursion and doesn’t work, but it’s for illustrating my question: how to encapsulate the decision which child class to initialize in the initialization of a parent class? Or is this altogether a stupid idea?

Though the use case is not very clear, I would have used factory design pattern to achieve something similar to this. A basic example can be:
class Person(object):
# Create objects based on some name:
#staticmethod
def factory(name):
if name== "Male":
return Male()
elif name== "Female":
return Female()
else:
return None
class Male(Person):
pass
class Female(Person):
pass
person = Person.factory('Male')
Another example on factory method design pattern

__init__ is not supposed to return anything (or rather: it has to return None). Imo it's not the best way of writing it, or as you put it "altogether a stupid idea". Is there a particular reason why it can't be an attribute?

Related

If you store optional functionality of a base class in a secondary class, should the secondary class subclass the base class?

I know the title is probably a bit confusing, so let me give you an example. Suppose you have a base class Base which is intended to be subclassed to create more complex objects. But you also have optional functionality that you don't need for every subclass, so you put it in a secondary class OptionalStuffA that is always intended to be subclassed together with the base class. Should you also make that secondary class a subclass of Base?
This is of course only relevant if you have more than one OptionalStuff class and you want to combine them in different ways, because otherwise you don't need to subclass both Base and OptionalStuffA (and just have OptionalStuffA be a subclass of Base so you only need to subclass OptionalStuffA). I understand that it shouldn't make a difference for the MRO if Base is inherited from more than once, but I'm not sure if there are any drawbacks to making all the secondary classes inherit from Base.
Below is an example scenario. I've also thrown in the QObject class as a 'third party' token class whose functionality is necessary for one of the secondary classes to work. Where do I subclass it? The example below shows how I've done it so far, but I doubt this is the way to go.
from PyQt5.QtCore import QObject
class Base:
def __init__(self):
self._basic_stuff = None
def reset(self):
self._basic_stuff = None
class OptionalStuffA:
def __init__(self):
super().__init__()
self._optional_stuff_a = None
def reset(self):
if hasattr(super(), 'reset'):
super().reset()
self._optional_stuff_a = None
def do_stuff_that_only_works_if_my_children_also_inherited_from_Base(self):
self._basic_stuff = not None
class OptionalStuffB:
def __init__(self):
super().__init__()
self._optional_stuff_b = None
def reset(self):
if hasattr(super(), 'reset'):
super().reset()
self._optional_stuff_b = None
def do_stuff_that_only_works_if_my_children_also_inherited_from_QObject(self):
print(self.objectName())
class ClassThatIsActuallyUsed(Base, OptionalStuffA, OptionalStuffB, QObject):
def __init__(self):
super().__init__()
self._unique_stuff = None
def reset(self):
if hasattr(super(), 'reset'):
super().reset()
self._unique_stuff = None
What I can get from your problem is that you want to have different functions and properties based on different condition, that sounds like good reason to use MetaClass.
It all depends how complex your each class is, and what are you building, if it is for some library or API then MetaClass can do magic if used rightly.
MetaClass is perfect to add functions and property to the class based on some sort of condition, you just have to add all your subclass function into one meta class and add that MetaClass to your main class
From Where to start
you can read about MetaClass here, or you can watch it here.
After you have better understanding about MetaClass see the source code of Django ModelForm from here and here, but before that take a brief look on how the Django Form works from outside this will give You an idea on how to implement it.
This is how I would implement it.
#You can also inherit it from other MetaClass but type has to be top of inheritance
class meta_class(type):
# create class based on condition
"""
msc: meta_class, behaves much like self (not exactly sure).
name: name of the new class (ClassThatIsActuallyUsed).
base: base of the new class (Base).
attrs: attrs of the new class (Meta,...).
"""
def __new__(mcs, name, bases, attrs):
meta = attrs.get('Meta')
if(meta.optionA){
attrs['reset'] = resetA
}if(meta.optionB){
attrs['reset'] = resetB
}if(meta.optionC){
attrs['reset'] = resetC
}
if("QObject" in bases){
attrs['do_stuff_that_only_works_if_my_children_also_inherited_from_QObject'] = functionA
}
return type(name, bases, attrs)
class Base(metaclass=meta_class): #you can also pass kwargs to metaclass here
#define some common functions here
class Meta:
# Set default values here for the class
optionA = False
optionB = False
optionC = False
class ClassThatIsActuallyUsed(Base):
class Meta:
optionA = True
# optionB is False by default
optionC = True
EDIT: Elaborated on how to implement MetaClass.
Let me start with another alternative. In the example below the Base.foo method is a plain identity function, but options can override that.
class Base:
def foo(self, x):
return x
class OptionDouble:
def foo(self, x):
x *= 2 # preprocess example
return super().foo(x)
class OptionHex:
def foo(self, x):
result = super().foo(x)
return hex(result) # postprocess example
class Combined(OptionDouble, OptionHex, Base):
pass
b = Base()
print(b.foo(10)) # 10
c = Combined()
print(c.foo(10)) # 2x10 = 20, as hex string: "0x14"
The key is that in the definition of the Combined's bases are Options specified before the Base:
class Combined(OptionDouble, OptionHex, Base):
Read the class names left-to right and in this simple case
this is the order in which foo() implementations are ordered.
It is called the method resolution order (MRO).
It also defines what exactly super() means in particular classes and that is important, because Options are written as wrappers around the super() implementation
If you do it the other way around, it won't work:
class Combined(Base, OptionDouble, OptionHex):
pass
c = Combined()
print(Combined.__mro__)
print(c.foo(10)) # 10, options not effective!
In this case the Base implementation is called first and it directly returns the result.
You could take care of the correct base order manually or you could write a function that checks it. It walks through the MRO list and once it sees the Base it will not allow an Option after it.
class Base:
def __init_subclass__(cls, *args, **kwargs):
super().__init_subclass__(*args, **kwargs)
base_seen = False
for mr in cls.__mro__:
if base_seen:
if issubclass(mr, Option):
raise TypeError( f"The order of {cls.__name__} base classes is incorrect")
elif mr is Base:
base_seen = True
def foo(self, x):
return x
class Option:
pass
class OptionDouble(Option):
...
class OptionHex(Option):
...
Now to answer your comment. I wrote that #wettler's approach could be simplified. I meant something like this:
class Base:
def __init_subclass__(cls, *args, **kwargs):
super().__init_subclass__(*args, **kwargs)
print("options for the class", cls.__name__)
print('A', cls.optionA)
print('B', cls.optionB)
print('C', cls.optionC)
# ... modify the class according to the options ...
bases = cls.__bases__
# ... check if QObject is present in bases ...
# defaults
optionA = False
optionB = False
optionC = False
class ClassThatIsActuallyUsed(Base):
optionA = True
optionC = True
This demo will print:
options for the class ClassThatIsActuallyUsed
A True
B False
C True

Initialize class as another class

I'd like to do some sort of "reversed inheritance", in which I have a class that is initialized as another class, according to input, and has several shared methods as well. I'm not interested in instantiating a different classes for each input, I'd like that to happen 'under the hood'.
I'm looking for the correct way to do the following:
class Carpenter():
def __init__(self):
self.tools = ['saw', 'screwdriver']
self.material = 'wood'
class Baker():
def __init__(self):
self.tools = ['oven', 'mixer']
self.material = 'flour'
class Professional():
def __init__(self, profession, name):
self.name = name
if profession == 'carpenter':
Carpenter.__init__(self)
elif profession == 'baker':
Baker.__init__(self)
def work(self):
print('working')
def go_home(self):
print('finally')
I could do it by performing 'by-the-book' inheritance and calling a function to handle the input-dependent logic:
class Professional():
...
class Carpenter(Professional):
...
class Baker(Professional):
...
def get_professional(profession):
if profession == 'baker':
professional = Baker()
elif profession == 'carpenter':
professional = Carpenter()
return professional
However I've been wondering if there's a more elegant way to do so.
Thanks!
You might want to use a dictionary:
professional = {'baker': Baker, 'carpenter': Carpenter}
then use;
def get_professional(profession):
constructor = professional['profession']
return constructor()
Another way would use the exec function but it would not be a good idea

Accessing Class variables from another class

How do you access an instance in an object and pass it to another 'main' object? I'm working with a parser for a file that parses different tags, INDI(individual), BIRT(event), FAMS(spouse), FAMC(children)
Basically there are three classes: Person, Event, Family
class Person():
def __init__(self, ref):
self._id = ref
self._birth : None
def addBirth(self, event):
self._birth: event
class Event():
def __init__(self, ref):
self._id = ref
self._event = None
def addEvent(self, event):
self._event = event
#**event = ['12 Jul 1997', 'Seattle, WA'] (this is generated from a function outside a class)
I want to transfer self._event from the Event class into addBirth method to add it into my person class. I have little knowledge on how classes and class inhertiances work. Please help!
If I understand your question, you want to pass an (for example) Event object to an instance of Person?
Honestly, I don't understand the intent of your code, but you probably just need to pass self from one class instance to the other class instance.
self references the current instance.
class Person:
def __init__(self):
self._events = []
def add_event(self, event)
self._events.append(event)
class Event:
def add_to_person(self, person):
person.add_event(self)
The most proper way to handle situations like this is to use getter and setter methods; data encapsulation is important in OO programming. I don't always see this done in Python where I think it should, as compared to other languages. It simply means to add methods to your classes who sole purpose are to return args to a caller, or modify args from a caller. For example
Say you have class A and B, and class B (caller) wants to use a variable x from class A. Then class A should provide a getter interface to handle such situations. Setting you work the same:
class class_A():
def __init__(self, init_args):
x = 0
def someMethod():
doStuff()
def getX():
return x
def setX(val):
x = val
class class_B():
def init(self):
init_args = stuff
A = class_A(init_args)
x = class_A.getX()
def someOtherMethod():
doStuff()
So if class B wanted the x property of an instance object A of class class_A, B just needs to call the getter method.
As far as passing instances of objects themselves, say if you wanted A to pass an already-created instance object of itself to a method in class B, then indeed, you simply would pass self.

Python decorator to limit a method to a particular class?

I've got a large library of Django apps that are shared by a handful of Django projects/sites. Within each project/site there is an option to define a 'Mix In' class that will be mixed in to one of the in-library base classes (which many models sub-class from).
For this example let's say the in-library base class is PermalinkBase and the mix-in class is ProjectPermalinkBaseMixIn.
Because so many models subclass from PermalinkBase, not all the methods/properities defined in ProjectPermalinkBaseMixIn will be utilitized by all of PermalinkBase's subclasses.
I'd like to write a decorator that can be applied to methods/properties within ProjectPermalinkBaseMixIn in order to limit them from running (or at least returning None) if they are accessed from a non-approved class.
Here's how I'm doing it now:
class ProjectPermalinkBaseMixIn(object):
"""
Project-specific Mix-In Class to `apps.base.models.PermalinkBase`
"""
def is_video_in_season(self, season):
# Ensure this only runs if it is being called from the video model
if self.__class__.__name__ != 'Video':
to_return = None
else:
videos_in_season = season.videos_in_this_season.all()
if self in list(videos_in_season):
to_return = True
else:
to_return False
return to_return
Here's how I'd like to do it:
class ProjectPermalinkBaseMixIn(object):
"""
Project-specific Mix-In Class to `apps.base.models.PermalinkBase`
"""
#limit_to_model('Video')
def is_video_in_season(self, season):
videos_in_season = season.videos_in_this_season.all()
if self in list(videos_in_season):
to_return = True
else:
to_return = False
return to_return
Is this possible with decorators? This answer helped me to better understand decorators but I couldn't figure out how to modify it to solve the problem I listed above.
Are decorators the right tool for this job? If so, how would I write the limit_to_model decorator function? If not, what would be the best way to approach this problem?
was looking at your problem and I think this might be an overcomplicated way to achieve what you are trying to do. However I wrote this bit of code:
def disallow_class(*klass_names):
def function_handler(fn):
def decorated(self, *args, **kwargs):
if self.__class__.__name__ in klass_names:
print "access denied to class: %s" % self.__class__.__name__
return None
return fn(self, *args, **kwargs)
return decorated
return function_handler
class MainClass(object):
#disallow_class('DisallowedClass', 'AnotherDisallowedClass')
def my_method(self, *args, **kwargs):
print "my_method running!! %s" % self
class DisallowedClass(MainClass): pass
class AnotherDisallowedClass(MainClass): pass
class AllowedClass(MainClass): pass
if __name__ == "__main__":
x = DisallowedClass()
y = AnotherDisallowedClass()
z = AllowedClass()
x.my_method()
y.my_method()
z.my_method()
If you run this bit of code on your command line the output will be something like:
access denied to class: DisallowedClass
access denied to class: AnotherDisallowedClass
my_method running!! <__main__.AllowedClass object at 0x7f2b7105ad50>
Regards

Virtual classes: doing it right?

I have been reading documentation describing class inheritance, abstract base classes and even python interfaces. But nothing seams to be exactly what I want. Namely, a simple way of building virtual classes. When the virtual class gets called, I would like it to instantiate some more specific class based on what the parameters it is given and hand that back the calling function. For now I have a summary way of rerouting calls to the virtual class down to the underlying class.
The idea is the following:
class Shape:
def __init__(self, description):
if description == "It's flat": self.underlying_class = Line(description)
elif description == "It's spiky": self.underlying_class = Triangle(description)
elif description == "It's big": self.underlying_class = Rectangle(description)
def number_of_edges(self, parameters):
return self.underlying_class(parameters)
class Line:
def __init__(self, description):
self.desc = description
def number_of_edges(self, parameters):
return 1
class Triangle:
def __init__(self, description):
self.desc = description
def number_of_edges(self, parameters):
return 3
class Rectangle:
def __init__(self, description):
self.desc = description
def number_of_edges(self, parameters):
return 4
shape_dont_know_what_it_is = Shape("It's big")
shape_dont_know_what_it_is.number_of_edges(parameters)
My rerouting is far from optimal, as only calls to the number_of_edges() function get passed on. Adding something like this to Shape doesn't seam to do the trick either:
def __getattr__(self, *args):
return underlying_class.__getattr__(*args)
What I am doing wrong ? Is the whole idea badly implemented ? Any help greatly appreciated.
I agree with TooAngel, but I'd use the __new__ method.
class Shape(object):
def __new__(cls, *args, **kwargs):
if cls is Shape: # <-- required because Line's
description, args = args[0], args[1:] #  __new__ method is the
if description == "It's flat": # same as Shape's
new_cls = Line
else:
raise ValueError("Invalid description: {}.".format(description))
else:
new_cls = cls
return super(Shape, cls).__new__(new_cls, *args, **kwargs)
def number_of_edges(self):
return "A shape can have many edges…"
class Line(Shape):
def number_of_edges(self):
return 1
class SomeShape(Shape):
pass
>>> l1 = Shape("It's flat")
>>> l1.number_of_edges()
1
>>> l2 = Line()
>>> l2.number_of_edges()
1
>>> u = SomeShape()
>>> u.number_of_edges()
'A shape can have many edges…'
>>> s = Shape("Hexagon")
ValueError: Invalid description: Hexagon.
I would prefer doing it with a factory:
def factory(description):
if description == "It's flat": return Line(description)
elif description == "It's spiky": return Triangle(description)
elif description == "It's big": return Rectangle(description)
or:
def factory(description):
classDict = {"It's flat":Line("It's flat"), "It's spiky":Triangle("It's spiky"), "It's big":Rectangle("It's big")}
return classDict[description]
and inherit the classes from Shape
class Line(Shape):
def __init__(self, description):
self.desc = description
def number_of_edges(self, parameters):
return 1
Python doesn't have virtual classes out of the box. You will have to implement them yourself (it should be possible, Python's reflection capabilities should be powerful enough to let you do this).
However, if you need virtual classes, then why don't you just use a programming language which does have virtual classes like Beta, gBeta or Newspeak? (BTW: are there any others?)
In this particular case, though, I don't really see how virtual classes would simplify your solution, at least not in the example you have given. Maybe you could elaborate why you think you need virtual classes?
Don't get me wrong: I like virtual classes, but the fact that only three languages have ever implemented them, only one of those three is still alive and exactly 0 of those three are actually used by anybody is somewhat telling …
You can change the class with object.__class__, but it's much better to just make a function that returns an instance of an arbitrary class.
On another note, all class should inherit from object unless you use using Python 3, like this, otherwise you end up with an old-style class:
class A(object):
pass

Categories