Using a function within a function - python

So I have a class with some functions. I want to use a function in another function to calculate the fuelconsumption.
I have the attribute self.consumption, which is calculated within the function Calculate_consumption.
Now I want to write a new function, which is updating the kilometer counter and also calculating if you are driving efficient.
So, I want to calculate the consumption by using the function Claculate_consumption and then see if it is bigger then 8 or not.
Well I tried to just write the function as I have found it here on Stackoverflow: How do you call a function in a function?
But this solution somehow does not work. Maybe somebody can point out my mistake.
class Car:
def __init__(self, kmDigit):
self.kmDigit = int(kmDigit)
self.Max = 8
self.consumption = 0
def Claculate_consumption(self, Liter, km):
self.consumption += (Liter/km)*100
return round(self.consumption, 2)
def Refuel(self,Liter, km):
self.kmDigit += km
print self.kmDigit
a = Claculate_consumption(Liter, km)
if a > self.Max:
b = self.consumption - self.Max
print 'Your fuel consumption is too high!'
else:
print 'Nice!'
I am getting a **NameError** in line 14, because Calculate_consumption is somehow a global name.

You have to write: a = self.Claculate_consumption(Liter, km)
because your program does not know where to look for this method. Self says that this method is in the same class in which you call the method
self : self represents the instance of the class. By using the "self" keyword we can access the attributes and methods of the class in python.
https://micropyramid.com/blog/understand-self-and-init-method-in-python-class/

Related

Updating Dependent Attributes After Mutation

Let's say I have the following classes:
import math
class LineSegment:
def __init__(
self,
origin,
termination,
):
self.origin = origin
self.termination = termination
self.length = self.calculate_length()
def calculate_length(self):
return math.sqrt(
(self.origin.x - self.termination.x) ** 2
+ (self.origin.y - self.termination.y) ** 2
)
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
An object of the LineSegment class is composed of two objects of the Point class. Now, let's say I initialize an object as so:
this_origin = Point(x=0, y=0)
this_termination = Point(x=1, y=1)
this_line_segment = LineSegment(origin=this_origin, termination=this_termination)
Note: The initialization of the line segment automatically calculates its length. This is critical to other parts of the codebase, and cannot be changed. I can see its length like this:
print(this_line_segment.length) # This prints "1.4142135623730951" to the console.
Now, I need to mutate one parameter of this_line_segment's sub-objects:
this_line_segment.origin.x = 1
However, the this_line_segments length attribute does not update based on the new origin's x coordinate:
print(this_line_segment.length) # This still prints "1.4142135623730951" to the console.
What is the pythonic way to implement updating a class's attributes when one of the attributes they are dependent upon changes?
Option 1: Getter and Setter Methods
In other object-oriented programming languages, the behavior you desire, adding additional logic when accessing the value of an instance variable, is typically implemented by "getter" and "setter" methods on all instance variables in the object:
class LineSegment:
def __init__(
self,
origin,
termination,
):
self._origin = origin
self._termination = termination
# getter method for origin
def get_origin(self):
return self._origin
# setter method for origin
def set_origin(self,new_origin):
self._origin = new_origin
# getter method for termination
def get_termination(self):
return self._termination
# setter method for termination
def set_termination(self,new_termination):
self._termination = new_termination
def get_length(self):
return math.sqrt(
(self.get_origin().x - self.get_termination().x) ** 2
+ (self.get_origin().y - self.get_termination().y) ** 2
) #Calls the getters here, rather than the instance vars in case
# getter logic is added in the future
So that the extra length calculation is performed every time you get() the length variable, and instead of this_line_segment.origin.x = 1, you do:
new_origin = this_line_segment.get_origin()
new_origin.x = 1
this_line_segment.set_origin(new_origin)
print(this_line_segment.get_length())
(Note that I use _ in front of variables to denote that they are private and should only be accessed via getters and setters. For example, the variable length should never be set by the user--only through the LineSegment class.)
However, explicit getters and setters are clearly a clunky way to manage variables in Python, where the lenient access protections make accessing them directly more transparent.
Option 2: The #property decorator
A more Pythonic way to add getting and setting logic is the #property decorator, as #progmatico points out in their comment, which calls decorated getter and setter methods when an instance variable is accessed. Since all we need to do is calculate the length whenever it is needed, we can leave the other instance variables public for now:
class LineSegment:
def __init__(
self,
origin,
termination,
):
self.origin = origin
self.termination = termination
# getter method for length
#property
def length(self):
return math.sqrt(
(self.origin.x - self.termination.x) ** 2
+ (self.origin.y - self.termination.y) ** 2
)
And usage:
this_line_segment = LineSegment(origin=Point(x=0,y=0),
termination=Point(x=1,y=1))
print(this_line_segment.length) # Prints 1.4142135623730951
this_line_segment.origin.x = 1
print(this_line_segment.length) # Prints 1.0
Tested in Python 3.7.7.
Note: We must do the length calculation in the length getter and not upon initialization of the LineSegment. We can't do the length calculation in the setter methods for the origin and termination instance variables and thus also in the initialization because the Point object is mutable, and mutating it does not call LineSegment's setter method. Although we could do this in Option 1, it would lead to an antipattern, in which we would have to recalculate every other instance variable in the setter for each instance variable of an object in the cases for which the instance variables depend on one another.

score calculator with python and pandas

I'm trying to make a simple app to get the score of anyone on a test from the following formula:
score=(grade-average)/variance
for example if your score out of 20 is 18 and the average of the class is 15 then this formula helps you to understand how is your grade in comparison to others.
my code opens an excel file located in my pc in reads the points column writes them to a list then gets the average and variance and uses the formula.
this is my excel file.
BTW the grades in the excel are just for testing.
I've tried these to codes(I'm not that pro in using classes and I was trying to make some use of it):
this is the first one
class taraz:
def __init__(self,file_name,file_dir,your_point):
self.file_name=file_name
self.file_dir=file_dir
self.your_point=your_point
def sum_ave():
f=pandas.read_excel(r (file_dir))
point_list=f['point'].tolist()
sum1=sum(point_list)
ave1=sum1/len(point_list)
def variance():
for i in point_list:
var1=sqrt(((i-ave1)**2)/len(point_list))
def taraz1():
taraz1=(your_point-ave1)/var1
print(taraz1)
print(taraz1)
this is the second one:
def taraz(file_name,file_dir,your_point):
def sum_ave():
f=pandas.read_excel(r (file_dir))
point_list=f['point'].tolist()
sum1=sum(point_list)
ave1=sum1/len(point_list)
def variance():
for i in point_list:
var1=sqrt(((i-ave1)**2)/len(point_list))
def taraz1():
taraz1=(your_point-ave1)/var1
print(taraz1)
from the first code I just got and output like this:
<main.taraz object at 0x02528130>
and from the second one I don't get an output at all.
will be glad to use your tips thanks anywas.
Firstly, understand the scope of variable. If a variable is declared inside a method it will be accessible from that method only unless it is declared as global.
When it comes to your code, variance method inside your class.
def variance():
for i in point_list:
var1=sqrt(((i-ave1)**2)/len(point_list))
How the variance method will understand point_lits variable. It is neither defined not declared as global/class variable.
Second, Methods of a class will take a default parameter usually defined as self unless it is decorated as a classmethod. check here to understand about self keyword.
Third, Classes will be having objects but functions won't. So you are not able to see the bound object with the function.
So, code after adding self keyword will look like this
from os import path
class Taraz:
def __init__(self, file_name, file_dir, your_point):
self.file_name = file_name
self.file_dir = file_dir
self.your_point = your_point
self.point_list = None
self.ave1 = None
def sum_ave(self):
f = pandas.read_excel(path.join(self.file_dir, self.file_name))
self.point_list = f['point'].tolist()
sum1 = sum(self.point_list)
self.ave1 = sum1 / len(self.point_list)
def variance(self):
if self.point_list is not None and self.ave1 is not None:
for i in self.point_list:
var1 = sqrt(((i-self.ave1)**2) / len(self.point_list))
def taraz1(self):
taraz1 = (self.your_point - self.ave1) / var1
print(taraz1)
Edit:
>>> def func():
... pass
...
>>> class cla:
... pass
...
>>> func()
>>>
>>> cla()
<__main__.cla object at 0x0000019A55944550>
>>> func
<function func at 0x0000019A552D2EA0>
>>> cla
<class '__main__.cla'>
() are used to call the method or function. Here func is a function and cla is a class. When you call a class it will return the object of the class so you see <main.cla object at 0x0000019A55944550>, but when you call function it will return the response of the function. Since my function is not having anything here, it retuned nothing.
There is no reason to use pandas to read from the excel file and then convert it into a list and re-implement basic vector operations such as mean and variance.
from os import path
import pandas as pd
class Taraz:
def __init__(self, filepath):
scores = pd.read_excel(filepath)['point']
self.mean = scores.mean()
self.var = scores.var()
def score(self, score):
return (score - self.mean) / self.var
if __name__ == '__main__':
taraz = Taraz(path.join('path', 'to', 'the', 'file.xlsx'))
print(taraz.score(16))
Output:
-0.012571428571428395
In your examples you have several errors that I would like to comment.
Variable scope is important. If you assign a variable inside a function, it will be assigned just inside that function. Outside of it will raise a NameError.
Methods first argument (which should be called self except for some special methods) is the instance itself, where we can assign values that will be stored in the instance and later retrieve them. For example, in the constructor (__init__) method of the code above, we are assigning somehting to self.mean and that value will be stored in the instance so that we can later use it in out score method.
OOP (Object Oriented Programming) is a very well stablished coding pattern, but trying to force the use of a class for something that doesn't really represent a type seems a bit unnecesary. This could be achieved in a single function easily:
from os import path
import pandas as pd
def taraz(filepath):
scores = pd.read_excel(filepath)
mean = scores['point'].mean()
var = scores['point'].var()
scores['scores'] = (scores['point'] - mean) / var
return scores
if __name__ == '__main__':
print(taraz(path.join('path', 'to', 'the', 'file.xlsx')))
Output:
name point scores
0 tghi 15 -0.163429
1 nghi 16 -0.012571
2 asghr 15 -0.163429
3 sbhn 20 0.590857
4 tghi 12 -0.616000
5 nghi 20 0.590857
6 asghr 17 0.138286
7 sbhn 18 0.289143
8 tghi 17 0.138286
9 nghi 16 -0.012571
10 asghr 15 -0.163429
11 12 -0.616000
As you can see, pandas dataframes implement vector operations, so (scores['point'] - mean) / var is translated to a vector of integers minus a float, divided by a float, and ther result of that operation is a vector of floats, that we store in the column 'scores'. This way we compute the scores for every row.

How to access the namespace of a function in python

How do I access the namespace of another module or function in python?
For example, let's say I have access to a function f that has been passed, and I want get a variable it has access to.
class function_creator():
def __init__(self, start_num):
self.number = start_num
def getfunc(self):
def nestedfunc():
print(f'The number you were trying to find was {self.number}')
self.number += 1
return nestedfunc
f = function_creator(5).getfunc()
Running f() will print the number, however I cannot find how to access it directly.
The way you're calling the code, there's not a good way to get at that number. But if you saved the instance of function_creator to a variable, you'd be able to get at it more easily:
fc = function_creator(5)
f = fc.getfunc()
f()
print(fc.number)
While I said there isn't an good way to access the value, it is possible by diving into the internals of the function object, as the function is a closure on the self variable from getfunc. You can get the value of that self argument from f using f.__closure__[0].cell_contents, and then get the number by checking its number attribute:
f = function_creator(5).getfunc()
f()
fc = f.__closure__[0].cell_contents
print(fc.number)
This is CPython specific, and probably won't work in other Python interpreters.

Calling a Variable from Another Function in Python

I have spent the past few hours reading around but I'm not really understanding what I am sure is a very basic concept: passing values (as variables) between different functions.
class BinSearch:
def __init__(self,length,leng,r,obj_function,middle):
self.length = length
self.leng = leng
self.r = r
self.obj_function = obj_function
self.middle = middle
self.objtobin(obj_function)
def BinarySearch(length,leng,r):
mid = np.arange(0,len(length),1)
middle = min(mid) + (max(mid)-min(mid))//2
L_size = []
L = length[middle]
L_size.append(L)
return L
def objtobin(self,obj_function):
# length,leng,middle = BinSearch.BinarySearch()
if (obj_function>=0.98):
return BinSearch.BinarySearch(self.length,min(leng),self.middle-1)
else:
return BinSearch.BinarySearch(self.length,self.middle+1,max(leng))
BinSearch.objtobin(obj_function=max(objectivelist))
When I run the above code, BinSearch.objtobin code gives "objtobin() missing 1 required positional argument: 'self'" What should I do for this error?
Thanks for help!
Firstly, thank you all for your help. But I do not understand how should I change this code
I have started modifying your code so that it would run without errors, but there are a few other mistakes in there as well, and I have not tried to make sense of all your parameters.
It would look something like this, but I will explain below.
# --- OP's attempt that fails ---
# BinSearch.objtobin(obj_function=max(objectivelist))
# -- -- -- -- -- -- -- -- -- -- --
# --- Using an instance ---
figure_this_out_yourself = 100
# this variable is a placeholder for any parameters I had to supply
myBinSearchInstance = BinSearch(
length = figure_this_out_yourself,
leng = [figure_this_out_yourself],
r = figure_this_out_yourself,
obj_function = figure_this_out_yourself,
middle = figure_this_out_yourself)
myBinSearchInstance.objtobin(obj_function = max(objectivelist))
There is one important concept to be grasped here: self.
Let us consider this simple example function here, which shall always output a number one larger than last time.
counter = 0
def our_function ():
global counter
counter = counter + 1
return counter
print(our_function())
It is okay as it is, but it uses a global variable to keep track of its state. Imagine using it for two different purposes at the same time. That would be chaos!
So instead, we package this inside a class.
# unfinished apprach
counter = 0
class OurClass:
# This is called a static method
def our_function ():
global counter
counter = counter + 1
return counter
print(our_function())
When we try to run this, we run into a problem.
NameError: name our_function is not defined
This happens because it is now accessible only within that class. So we need to call it as
print(OurClass.our_function())
That makes it okay to have functions with the same name around - as long as they are in different classes - but it does not solve our chaos for using our_function multiple times at once. What we want is basically to have two independent counter variables. This is where instances come into play: Of course we could manually create a second function that uses a second global variable, but that gets out of hand quickly when you use it more and more.
So let's move counter inside our class.
class OurClass:
counter = 0
def our_function ():
global counter
counter = counter + 1
return counter
You guessed it - now counter is no longer defined:
NameError: name counter is not defined
So let us pass the instance variable that we want to use into the function as a parameter. And then use that instance to get its counter:
class OurClass:
counter = 0
def our_function (the_instance):
the_instance.counter = the_instance.counter + 1
return the_instance.counter
myInstance = OurClass()
mySecondInstance = OurClass()
print(OurClass.our_function(myInstance))
print(OurClass.our_function(mySecondInstance))
And successfully, both print statements print 1!
But that is a bit annoying because this the_instance is something that is not like the other arguments. To make it distinct, python allows us to avoid the first parameter and instead provide it as the receiver. Both of these work:
print(myInstance.our_function())
print(OurClass.our_function(mySecondInstance))
Python uses a very strong convention for these parameters. Instead of the_instance, call it self. See Why is self only a convention?.
class OurClass:
counter = 0
def our_function (self):
self.counter = self.counter + 1
return self.counter
myInstance = OurClass()
mySecondInstance = OurClass()
print(myInstance.our_function())
print(mySecondInstance.our_function())
Now we're almost done! Just one thing left to understand: Where do the parameters of __init__() come from?
They are passed to __init__() from the line where we construct it. So let me demonstrate by adding a starting value for our counter:
class OurClass:
counter = 0
def __init__ (self, starting_value):
self.counter = starting_value
def our_function (self):
self.counter = self.counter + 1
return self.counter
myInstance = OurClass(5)
mySecondInstance = OurClass(10)
print(myInstance.our_function())
print(OurClass.our_function(mySecondInstance))
This prints 6 and 11.
But what do those comments mean with #staticmethod? For that, see Difference between staticmethod and classmethod and Do we really need #staticmethod decorator in python to declare static method
.
In short: You can annotate any method in a class with either #staticmethod or #classmethod.
#staticmethod means that it can be called like myInstance.foo() when OurClass.foo() does not take self as a parameter. Without that decorator, you could only call it as OurClass.foo() but not as myInstance.foo().
#classmethod means that it can be called like myInstance.foo() and it does not get myInstance as the first parameter, but instead the class of myInstance, which is OurClass. That allows you e.g. to define alternative constructors. Also, a class method is not inherited when you subclass it, so it won't be mistakenly called.
The comments are pointing out that you could also use a #staticmethod and avoid creating an instance. For that, you would have to not use any variables in the class itself - but you aren't using those for long anyways, so you could all pass them as parameter to the function.

Python class Inheritance Initialization

I have the following example setup:
class Feet:
def __init__ (self, value = 0.0):
self.value = value
self.units = "f"
def feet(self):
return self.value
class Meters:
def __init__(self, value = 0.0):
self.value = value
self.units = "m"
def feet(self):
# This is probably not an accurate conversion.
return self.value * 2.54 * 10
class Distance (Feet, Meters):
def __init__(self, type = Feet()):
Feet.__init__(self)
Meters.__init__(self)
print type.feet() -- Prints 254.0
self = type
print self.feet() -- Prints 254.0
dist = Distance(Meters(10.0))
print dist.units -- Prints = "m"
print dist.value -- Prints = 0.0
print dist.feet() -- Prints = 0.0
I can't seem to understand why when I initialize the class to a Meters Class type, and assign it 10.0, I don't keep the 10.0. However the Units seem to have stayed correct. Am I missing something about how this is being setup?
My understanding is that I'm creating an "instance" of Meters, and assigning it to the "self" variable of Distance. If the self value couldn't be modified I could understand if my units was "f", but my units is "m" so it's obviously assigning the Meters class to self, but it's not taking the instantiated values, which I find quite odd.
To be honest I don't even know what I would google in this case, so I apologize I haven't done a whole lot of googling, most of what I found didn't apply at all to this type of problem.
Additionally, my plan was to basically "cast" it to the same type no matter what you passed in, for example for feet I would return the self instance for the Feet class, and in the Meters class I would return Feet(self.Value * 2.54 * 10) so I would always have my distance in Feet.
so for Feet feet becomes
def feet(self):
return self
for Meters feet becomes
def feet(self):
return Feet(self.value * 2.54 * 10)
To Recap, is there a reason that I'm able to pass in 1 of 2 classes as part of initialization, but it doesn't take my initialization parameters for that class?
It's really unclear to me why I can assign "self" in the distance class, and before it returns it appears to have the right initialization but upon returning it doesn't work right.
The thing is that you are inheriting from 2 classes Feet and Meters. Both classes have the same methods. In your Distance.__init__() method, you are overriding Feet's methods with Meters' methods when doing this:
Feet.__init__(self)
Meters.__init__(self)
What I would have done differently:
class Distance(object):
def __init__(self, meters=None, feet=None):
self.feet = feet
self.meters = meters
Then you can do something like:
distance = Distance(meters=Meters(12))
print distance.meters.value
print distance.meters.type
# Here do whatever you want with them
You can pass in the two objects at the same time. And do some other stuff with
the two objects if the are both different than None.
There's absolutely no reason to inherit from either Feet or Meters here, let alone both. This is a classic case of composition, rather than inheritance, especially since you are passing the units class as a parameter. Remove that subclassing, and in __init__ you can do self.type = type.
Other answers cover the problems you have with inheriting, but haven't covered your rebinding of self.
Inside a method (such as __init__), self is simply a local name bound to the instance. You are perfectly at liberty to rebind the name, but that simply makes self refer to something else. It doesn't affect the instance.
In this case, when __init__ returns the self name goes out of scope, but the original instance is assigned to dist just as though you hadn't rebound the name self.
Note that __init__ is an initializer, not a constructor. Python does also allow you to define a custom constructor for a class (__new__), and the constructor can change the object that is returned. However you don't need to use it here.
This line:
self = type
doesn't do what you think it does. You think this is an assignment statement, in which the object refered to by self takes on the attributes of type, a la C++.
Python doesn't have assignments in the same sense that other languages have.
What that line does is to bind the local name self to the object to which type is currently bound. It has absolutely no effect outside of Distance.__init__(), and virtually no effect on the object to which self was previously bound.

Categories