I'm sure this will be a duplicate question, but I can't seem to find the words to locate one.
I have a set of very similar models I'd like to code up. The models are all the same, apart from a single function / line of code. I'd like to avoid any code repetition. Let' see an MWE:
import numpy as np
class SinModel:
def __init__(self):
self.x = np.linspace(-np.pi, np.pi)
def run(self):
# Computations which are invariant of the function we use later
self.y = np.sin(self.x)
# More computations which are invariant of which funcion was used
Our second model will involve the same series of computations, but will use a different function mid way though (here, cosine instead of sine):
class CosModel:
def __init__(self):
self.x = np.linspace(-np.pi, np.pi)
def run(self):
# Computations which are the same as in SinModel
self.y = np.cos(self.x)
# More computations which are the same as in SinModel
Here I have lots of code repetition. Is there a better way to implement these models? I was hoping it would be possible to create a class Model which could inherit the differing function from an arbitrary class.
An important note is that the function which changes between models may take different arguments from self depending on the model.
The words you're looking for are inheritance (allowing a class to inherit and extends / specialize a parent class) and the "template method" design pattern (which is possibly the most common design pattern - the one everyone discovers by itself long before reading about design patterns).
Expanding on your MWE:
import numpy as np
class ModelBase(object):
def __init__(self):
self.x = np.linspace(-np.pi, np.pi)
def run(self):
# Computations which are invariant of the function we use later
self.y = self.compute_y()
# More computations which are invariant of which funcion was used
def compute_y(self):
raise NotImplementedError("class {} must implement compute_y()".format(type(self).__name__))
class SinModel(ModelBase):
def compute_y(self):
return np.sin(self.x)
class CosModel(ModelBase):
def compute_y(self):
return np.cos(self.x)
This being said, creating instance attributes outside the initializer (the __init__ method) is considered bad practice - an object should be fully initialized (have all it's attributes defined) when the initializer returns, so it might be better to move the self.y = self.compute_y() line to the initializer if possible, or, if self.y always only depends on self.x, make it a computed attribute:
class ModelBase(object):
def __init__(self):
self.x = np.linspace(-np.pi, np.pi)
#property
def y(self):
return self._compute_y()
def _compute_y(self):
raise NotImplementedError("class {} must implement _compute_y()".format(type(self).__name__))
def run(self):
# Computations which are invariant of the function we use later
# no need to explicitely set self.y here, just use `self.y`
# and it will delegate to self._compute_y()
#(you can't set it anymore anyway since we made it a readonly propery)
# More computations which are invariant of which funcion was used
class SinModel(ModelBase):
def _compute_y(self):
return np.sin(self.x)
class CosModel(ModelBase):
def _compute_y(self):
return np.cos(self.x)
Also at this point you don't necessarily need subclasses anymore, at least if that's the only thing that changes - you can just pass the proper function as a callback to your model class ie:
class Model(object):
def __init__(self, compute_y):
self.x = np.linspace(-np.pi, np.pi)
self._compute_y = compute_y
#property
def y(self):
return self._compute_y(self)
def run(self):
# code here
cos_model = Model(lambda obj: np.cos(obj.x))
cos_model.run()
sin_model = Model(lambda obj: np.sin(obj.x))
sin_model.run()
Yes, and there's even a name for it: Inheritance is the idea that child classes can "inherit" behaviors and attributes from parent classes, and Polymorphism is the idea that two child classes, sharing similar behavior, can have different implementations of the same method - so that you can call a method on an object without knowing explicitly what type it is, and still have it do the right thing.
Here's how you'd do that in python:
class TrigModel:
def __init__(self):
self.x = np.linspace(-np.pi, np.pi)
def run(self):
raise NotImplementedError("Use subclasses SinModel or CosModel")
class SinModel(TrigModel):
#override
def run(self):
self.y = np.sin(self.x)
class CosModel(TrigModel):
#override
def run(self):
self.y = np.cos(self.x)
Unless you explicitly specify otherwise (by declaring a method like run() that overrides the parent class's method of the same name), SinModel and CosModel will call TrigModel's methods on themselves (in this case, they both call TrigModel's constructor, but then display different behavior when you call run() on them).
If you then do:
model.run()
then model will behave differently depending on whether it's a SinModel or a CosModel, depending on what you set it to beforehand.
The #override decorator isn't strictly necessary, but it's good practice to lessen ambiguity.
Related
I need to split class methods in several files. Functionality need to by that I can pass inside method all variables defined in self and receive new self variables defined inside the method.
My attempt:
Below code works, but I don't know if this is the best/proper solution.
Base:
from calculate_function import function
class Data():
def __init__(self):
self.y = -2
self.x = 1
self.z, self.result = function(self)
calculate_function.py:
def function(self):
z = 2
result = z + self.x
return z, result
For above I pass self inside new function for collect all init variables, then define new self variable/results.
There will by much more functions inside different files that will done some calculations and create new variables for instance of class.
Question
What I need is to pass each created self variable to each function.
For above code the solution is proper defined or there is better option to this?
If you want to externalize some part of your class code to external functions, it's better to write those as pure functions and keep the attribute access (and even more attributes updates) within the class code itself - this makes the code much easier to test, read and maintain. In you case this would looks like:
from calculate_function import function
class Data():
def __init__(self):
self.y = -2
self.x = 1
self.z, self.result = function(self.x)
calculate_function.py:
def function(x):
z = 2
result = z + x
return z, result
The points here are that 1/ you can immediatly spot the creation of attributes z and result and 2/ you can test function() without a Data instance.
I need to split class methods in several files.
This often means your class has too many responsabilities. Some parts of it can be delegated to pure functions like shown above. Some other parts, that need access to a common subset of your class attributes, can be delegated to other, smaller, specialized classes - but preferably using composition / delegation instead of inheritance (depending on concrete use cases of course).
You dont need pass self inside the function
Why not do it like this:
class Data():
def __init__(self):
self.y = -2
self.x = 1
self.function()
def function(self):
self.z = 2
self.result = self.z + self.x
Do wish to use another Class function or just a stand alone function?
Here is solution, using class inheritance:
-- function1.py --
class FunctionClass1():
def function1(self):
self.result = self.x + self.y
-- function2.py --
class FunctionClass2():
def function2(self):
self.result = self.result + self.z
-- data.py --
from function1 import FunctionClass1
from function2 import FunctionClass2
class Data(FunctionClass1, FunctionClass2):
def __init__(self):
self.x = 1
self.y = 2
self.z = 3
self.function1()
self.function2()
I have created four classes: experiment, experiment_type1, experiment_type2 and experiment_type3
experiment is an abstract class, it cannot be instantiated. It has 2 methods, __init__(self) and run(self) where run(self) is abstract.
experiment_type1 and experiment_type2 are derived from experiment. They inherit the __init__(self) from experiment (so they share the same constructor) but they implement run(self) differently from each other.
My problem is with experiment_type3 class. It also only has the run(self) method, implemented differently from experiment_type1 and experiment_type2, but its constructor takes an additional argument. Its constructor is of the form __init__(self, parameter)
Ideally I would like experiment_type3 to be derived from experiment. But there is a constructor mismatch. What is the best way to handle this? Programming in python in this case.
Edit:
This is the code for experiment and experiment_type3. As you can see it depends on self.epsilon which does not exist.
import numpy as np
from abc import ABC, abstractmethod
from Bandit import Bandit
class experiment(ABC):
def __init__(self, num_iter, bandit_list): #epsilon is the chance to explore, num_iter is num of iterations, bandit_list is the list of the bandits
self.num_iter = num_iter
self.bandit_list = bandit_list
self.best_bandit = np.random.choice(len(bandit_list))
#abstractmethod
def run(self):
raise NotImplementedError('derived class must implement run() method!')
class eg_experiment(experiment):
def run(self):
for iteration in range(self.num_iter):
bandit = np.random.choice(len(self.bandit_list))
if(np.random.random() > self.epsilon):
bandit = self.best_bandit
self.bandit_list[self.best_bandit].pull()
self.best_bandit = np.argmax([bandit.current_mean for bandit in self.bandit_list])
As the comments point out, using super() on the parent class's __init__ should give you what you want.
class A:
def __init__(self, parameter)
self.parameter = parameter
class B(A):
def __init__(self, parameter, new_parameter)
super().__init__(parameter)
self.new_parameter = parameter
or in your case
class eg_experiment(experiment):
def __init__(num_iter, bandit_list, epsilon):
super().__init__(num_iter, bandit_list) #Python3
# super(experiment,self).__init__(num_iter, bandit_list) # Pythyon2.7
self.epsilon = epsilon
There's a saying: "If your class only has two methods, and one is __init__, use a function instead." Especially here, where there is no common implementation of the run method. You just have a bunch of independent functions with similar signatures.
def run_experiment_one(num_iter, bandit_list):
...
def run_experiment_two(num_iter, bandit_list):
...
def run_experiment_three(num_iter, bandit_list, epislon):
...
So I have in my project two classes: Circuit and SubCircuit. At some point I may need to construct a SubCircuit from a Circuit. Is there a more elegant way to do that than what's done below in the last 4 lines? Circuit may get some new attributes at some point for example, which would mean that conversion would also need to be updated - basically inviting bugs.
class Gate(abc.ABC):
def __init__(self, size):
self.in_ports = (InPort(self),) * size
self.out_ports = (OutPort(self),) * size
class Circuit:
def __init__(self, size):
self.input = (OutPort(self),) * size
self.output = (InPort(self),) * size
self.gates = []
# ... some other stuff that messes around with these attributes
class SubCircuit(Gate, Circuit):
def __init__(self, circuit=None):
Gate.__init__(self, size)
Circuit.__init__(self, size)
if circuit is not None:
self.gates = circuit.gates
self.input = circuit.input
self.output = circuit.output
Bugs are already there - when you do self.gates = circuit.gates, circuit.gates being a list, yu point both references to the same list - and if this list is updated on the original circuit, this update will be reflected in your subcircuit instance.
I think the most sane pattern tehre is to have an alternate constructor for the class if you have a circuit instance from which to update your own:
from copy import copy
class SubCircuit(Gate, Circuit):
def __init__(self, size):
Gate.__init__(self, size)
Circuit.__init__(self, size)
#classmethod
def from_circuit(cls , circuit, size):
self = SubCircuit(size)
for key, value in circuit.__dict__.items():
setattr(self, key, copy(value))
return self
One "right" thing to do is to make the classes __init__ and other methods calling each other through the collaborative use of super() instead of calling then explicitly by name - however, if your classes and subclasses are fixed to these 3, that may be a bit overkill, because Python's objects not handling extra parameters passed to its own __init__ method. (So, you'd have to verify in your base classes __init__ if they are the last ones prior to object on the Method Resolution Order, and swallow the remaining args)
class ThirdPartyA(object):
def __init__(self):
...
def ...():
...
-------------------
from xxx import ThirdPartyA
class ThirdPartyB(object):
def a(self):
...
#call to ThirdPartyA
....
def b(self):
...
#call to ThirdPartyA
...
def c(self):
...
#call to ThirdPartyA
...
-----------------------------------
from xxx import ThirdPartyA
class MyCodeA(ThirdPartyA):
def __init__(self):
# overriding code
When overriding the __init__ method of A class, how could I instruct B class that it should call MyCodeA instead of ThirdPartyA in all its methods?
The real code is here:
CLass Geoposition: ThirdPartyA
Class GeopositionField: ThirdPartyB
My override to class Geoposition so it returns max 5 decimal digits:
class AccuracyGeoposition(Geoposition):
def __init__(self, latitude, longitude):
if isinstance(latitude, float) or isinstance(latitude, int):
latitude = '{0:.5f}'.format(latitude)
if isinstance(longitude, float) or isinstance(longitude, int):
longitude = '{0:.5f}'.format(longitude)
self.latitude = Decimal(latitude)
self.longitude = Decimal(longitude)
From your updated code, I think what you're trying to do is change GeopositionField. to_python() so that it returns AccuracyGeoposition values instead of Geoposition values.
There's no way to do that directly; the code in GeopositionField explicitly says it wants to construct a Geoposition, so that's what happens.
The cleanest solution is to subclass GeopositionField as well, so you can wrap that method:
class AccuracyGeopositionField(GeopositionField):
def topython(self, value):
geo = super(AccuracyGeopositionField, self).topython(value)
return AccuracyGeoposition(geo.latitude, geo.longitude)
If creating a Geoposition and then re-wrapping the values in an AccuracyGeoposition is insufficient (because accuracy has already been lost), you might be able to pre-process things before calling the super method as well/instead. For example, if the way it deals with list is not acceptable (I realize that's not true here, but it serves as a simple example), but everything else you can just let it do its thing and wrap the result, you could do this:
class AccuracyGeopositionField(GeopositionField):
def topython(self, value):
if isinstance(value, list):
return AccuracyGeoposition(value[0], value[1])
geo = super(AccuracyGeopositionField, self).topython(value)
return AccuracyGeoposition(geo.latitude, geo.longitude)
If worst comes to worst, you may have to reimplement the entire method (maybe by copying, pasting, and modifying its code), but hopefully that will rarely come up.
There are hacky alternatives to this. For example, you could monkeypatch the module to globally replace the Geoposition class with your AccuracyGeoposition class But, while that may save some work up front, you're almost certain to be unhappy with it when you're debugging things later. Systems that are designed for aspect-oriented programming (which is basically controlled monkeypatching) are great, but trying to cram it into systems that were designed to resist it will give you headaches.
Assuming your real code works like your example—that is, every method of B creates a new A instance just to call a method on it and discard it—well, that's a very weird design, but if it makes sense for your use case, you can make it work.
The key here is that classes are first-class objects. Instead of hardcoding A, store the class you want as a member of the B instance, like this:
class B(object):
def __init__(self, aclass=A):
self.aclass = aclass
def a(self):
self.aclass().a()
Now, you just create a B instance with your subclass:
b = B(OverriddenA)
Your edited version does a different strange thing: instead of constructing a new A instance each time to call methods on it, you're calling class methods on A itself. Again, this is probably not what you want—but, if it is, you can do it:
class B(object):
def __init__(self, aclass=A):
self.aclass = aclass
def a(self):
self.aclass.a()
However, more likely you don't really want either of these. You want to take an A instance at construction time, store it, and use it repeatedly. Like this:
class B(object):
def __init__(self, ainstance):
self.ainstance = ainstance
def a(self):
self.ainstance.a()
b1 = B(A())
b2 = B(OverriddenA())
If this all seems abstract and hard to understand… well, that's because we're using meaningless names like A, B, and OverriddenA. If you tell us the actual types you're thinking about, or just plug those types in mechanically, it should make a lot more sense.
For example:
class Vehicle(object):
def move(self):
print('I am a vehicle, and I am moving')
class Operator(object):
def __init__(self, vehicle):
self.vehicle = vehicle
def move(self):
print('I am moving my vehicle')
self.vehicle.move()
class Car(object):
def move(self):
print('I am a car, and I am driving')
driver = Operator(Car())
driver.move()
class Complex:
realpart,imagpart=0,0
def __init__(self):
self.r = Complex.realpart
self.i = Complex.imagpart
x = Complex()
the above code works, x.r,x.i = (0,0), but when the class name is big, Class_name.Data_member way of accessing class data looks very redundant, is there any way to improve the class scoping, so I don't have to use Complex.imagpart? just use self.r = realpart?
This is what you want to do:
class Complex(object):
def __init__(self, realpart=0, imagpart=0):
self.realpart = realpart
self.imagpart = imagpart
Accessing the member variables is the "self.realpart" call. What you were using is class attributes which are accessed like this:
Complex.some_attribute
No. The data members you specified above are attributes of the class, and therefore require the class name to be specified. You could use self.r = self.__class__.realpart if you prefer. It seems as though you're just using these values as initializers though, so having realpart and imagpart at all is redundant.
(Also, note that Python has native support for complex numbers. Just write them such as 5+3j.)