pass arguments inside splitted methods - python

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()

Related

How to implement a sole class of methods inside an external class

I have an external class to represent my data idk. Inside the class idk I want to have another class change which will contain various methods to change instances of the class idk. What I've tried is below. Note: this is a simplified example of my actual code
class idk:
def __init__(self):
self.x=1
self.y=2
class change:
def func(self):
self.x=10
self.y=100
var=idk()
var.change.func()
print(var.x, var.y)
However this gives the error:
TypeError: func() missing 1 required positional argument 'self'
How do I change the code to make it work?
Well, first of all, your are getting this error because you are accessing the func function as a class attribute and not by an instance of it (putting a class definition inside another class won't make it an instance).
If it makes sense, you cloud put those "change methods" in the idk class directly (that would be a normal approach):
class idk:
def __init__(self):
self.x = 1
self.y = 2
def func(self):
self.x = 10
self.y = 100
var = idk()
var.func()
print(var.x, var.y) # Output: 10 100
If you really want/need to separate those methods, you could do another class. The way I would implement that class is with static methods where all of them recieve an idk instance as the first parameter:
class idk:
def __init__(self):
self.x = 1
self.y = 2
class idkChanger:
#staticmethod
def func(idk_obj):
idk_obj.x = 10
idk_obj.y = 100
var = idk()
idkChanger.func(var)
print(var.x, var.y) # Output: 10 100
If you really really want/need to have that "changer" class inside of the idk class you can define it there, but this is not common at all. Also, you will have to pass the instance as well, that Changer class:
class idk:
def __init__(self):
self.x = 1
self.y = 2
class Changer:
#staticmethod
def func(idk_obj):
idk_obj.x = 10
idk_obj.y = 100
var = idk()
idk.Changer.func(var)
print(var.x, var.y) # Output: 10 100
Final notes:
You could not mark (decorate) the func as static and it will work the same, but this will bring more confution for several reasons (e.g., you would tecnically saying that func is an instance method. Which is not, because the objects you want to change are not Change's instances but idk's).

Python inherit method from arbitrary class

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.

What are the options available to store variables and functions as separate cases in python?

I'm writing a mathematical model in python, the model has ~20 input parameters that i'm storing in a separate file. I Have multiple cases that I would like to evaluate/modify.
Each case is a ".py" file that holds variables and a few simple functions. What is the bast way to import them into my main Code?
So far i've been using "from CaseName import *" to import all the variables and functions from the case. However this does not lend itself to allowing for looping over the cases.
I've looked at using importlib but some sections of my code require that the imported variables are global. Are there any other alternatives?
Here is a simplified example.
Case1.py:
CaseName = "Case1"
a = 1.0
b = 1.0
def InitialConditions(x):
return(10*x)
Case2.py:
CaseName = "Case1"
a = 2.0
b = 2.0
def InitialConditions(x):
return(4*x**2)
MainCode.py:
from Case1 import *
RunCalculation(a,b)
etc....
Use classes:
class Case1(object):
def __init__(self, x):
self.a = 1
self.b = 1
self.x = x
def initial_condition(self):
print(10 * self.x)
# Initialize and use
case = Case1(x=3)
case.initial_condition()
# Rinse and repeat for all needed classes
If you start littering your environment with globals, it's probably an indication you should be using a class. If any of your models share a significant number of attributes (input parameters) or methods (functions) then look into extending your classes with inheritance.
For instance, if your RunCalculation function is always the same, it might be more efficient to create a parent class that contains it as a method and then inherit from that to make your individual cases, like so:
class ParentCase(object):
def __init__(self, a, b, x):
self.a = a
self.b = b
self.x = x
def run_calculation(self):
print(self.a + self.b + 35)
class Case1(ParentCase):
def __init__(self, x):
self.a = 1
self.b = 1
self.x = x
def initial_condition(self):
print(10 * self.x)
case = Case1(x=3)
case.run_calculation() #inherited method

Calling class-specific method from another class

I am working in a class called AlgoSystem, which is given strategy_0 and strategy_1 as inputs under initialization as well as the number of strategies (2 in this case). The strategy classes are stored in a dictionary called "strategies" within the AlgoSystem. Both strategy_0 and strategy_1 are different classes themselves, but both with a function called "__on_tick". These functions I want to call from within the AlgoSystem class.
My current attempt to do this is seen below:
class AlgoSystem:
def __init__(self, strategy_0, strategy_1, numstrategies):
self.var= 1
self.strategies = {0 : strategy_0,
1 : strategy_1}
self.num_strategies = numstrategies
def start(self):
for i in range(self.num_strategies):
self.strategies[i].__on_tick(self.var)
class Strategy_zero:
def __init__(self, x):
self.x = x
def __on_tick(self, var):
self.x = self.x + var
print(self.x)
class Strategy_one:
def __init__(self, y):
self.y = y
def __on_tick(self, var):
self.y = self.y - var
print(self.y)
strategy_0 = Strategy_zero(2)
strategy_1 = Strategy_one(4)
num_strategies = 2
system = AlgoSystem(strategy_0, strategy_1, 2)
system.start()
When I run the code above, I am given the error:
Strategy_zero' object has no attribute '_AlgoSystem__on_tick'
Apparently I'm not calling the class-functions "__on_tick" properly. How should I do this? I need to do it in a way, so I keep track on the changes of the two sub-classes (strategy_0 and strategy_1) through my defined dictionary within AlgoSystem: "strategies".
The double underscore prefix is specifically designed to prevent you from doing exactly what you are doing.
There is no reason for you to use it here. Remove the prefix and can your methods just on_tick.
Double underscore names are hidden names (hidden by obfuscation). I suggest having your on_tick method be called on_tick and try again.
The following code might help clarify what's going on with name-mangling.
class A:
def __mangled(self):
print "Class A name-mangled method"
class B:
def __init__(self):
a = A()
try:
a.__mangled()
except AttributeError:
# an attempt to access a name-mangled method assumes that
# the '_{class_name}' prefix should use 'B' as the class name
print "A instance has no attribute '_B__mangled'"
a._A__mangled()
# prints "Class A name-mangled method"
getattr(a, '_{}__mangled'.format(a.__class__.__name__))()
# same thing, but can be done without knowing the class name
B()
So, you could update self.strategies[i].__on_tick(self.var) to be:
strat = self.strategies[i]
getattr(strat, '_{}__on_tick'.format(strat.__class__.__name__)(self.var)
But, it would probably be best to not precede __on_tick with a double-underscore since it is intended to be accessed outside the class/instance.

Python: Am I using "self" correctly?

I'm new to Python and am only now starting to see the uses of self and would like to know if I am using it correctly. I have some sample code below and was wondering if someone could just skim through and see if it's the correct usage. I'm not sure if it's correct because I seem to be needing to use self a lot but perhaps that is just the style of the language. Thank You
Code
from tkinter import Canvas
class BouncyBall:
def __init__(self):
self.x = 0
self.y = 0
self.d = 15
self.color = 'blue'
self.speed = 2
self.move_left = False
self.move_right = False
def __init__(self, x, y, d, color):
self.x = x
self.y = y
self.d = d
self.color = color
self.speed = 2
self.move_left = False
self.move_right = False
#Accessor Methods
def get_x(self):
return self.x
def get_y(self):
return self.y
def get_diameter(self):
return self.d
def get_color(self):
return self.color
def get_speed(self):
return self.speed
def moving_right(self):
return self.move_right
def moving_left(self):
return self.move_left
#Mutator Methods
def set_x(self, x):
self.x = x
def set_y(self, y):
self.y = y
def set_diameter(self, d):
self.d = d
def set_color(self, color):
self.color = color
def set_speed(self, speed):
self.speed = speed
def set_move_right(self, move_right):
self.move_right = move_right
def set_move_left(self, move_left):
self.move_left = move_left
def draw_ball(self, canvas):
if isinstance(canvas, Canvas):
canvas.create_oval(self.x, self.y, self.x + self.d, self.y + self.d, fill=self.color)
else:
print("Improper Parameter Sent In")
You are using self correctly. It does tend to appear a lot in Python compared with other languages like C++ where the this parameter is implicit.
However, in Python it is not conventional to write get() and set() methods for everything, as you have done. You can reduce your code quite a bit by removing those--and remove a lot of selfs in the process.
Well, first off your __init__ is wrong. Python doesn't allow two definitions of a function/method with the same name in the same namespace, whether or not the prototype differs. I'd suggest dropping your first definition, and changing the def line of the second to:
def __init__(self, x=0, y=0, d=15, color='blue'):
which will do what you wanted (allow you to initialize without arguments by using default values).
You also probably want to drop all your set_ and get_ methods. If the attributes are read/write, just access them normally without getters and setters. If at some later point you need to make them read-only, or compute them, you can rename the attribute to have a leading underscore (e.g. _x) and use #property decorators to continue providing attribute-like access (with or without writability). That would instantly remove the vast majority of (unnecessary) accessor and mutator methods that make you reference self so often. For example, if x should be read-only, you'd set self._x = x in your __init__, then define a property:
#property
def x(self):
return self._x
and users would continue to read it as if it were a simple attribute, they just couldn't write it by accident (they could directly write _x, but that's their problem; Python's philosophy is that we're all adults, and if you ignore the convention that underscore prefixes are internal implementation details, the consequences are on your head).
Otherwise, yes, you'll be referencing self a lot. Python prefers explicit namespacing to implicit, so you use self to differentiate between instance access and scoped variable access. If you are going to be using a given variable a lot (and it won't change during the course of a method), you can cache it to a local name and use the local name unqualified, e.g.:
def some_method(self):
# We use x a lot and never change it, so cache up front:
x = self.x
# Can read cached x over and over without qualification for rest of method

Categories

Resources