Calculate attributes with subclass parameters - python

I'm working in small Python program with several classes and subclasses. The main point is that I need to calculate the value of the main class with the attributes of the subclasses.
class Product:
class Extra_1:
value = 5
base_value = 25
final_value = base_value + Extra1.value
The expected output for base_value it would be 30.
However I need to call this class from other file through an import, when I do that I'm not able to get the expected output (50) Instead of that I get 30. It seems like Python its not calculating the value with the formula.
import myprogram
myprogram.Product.Extra.value = 25
print(myprogram.Product.final_value) #Output = 30
I tried to create a function to calculate the final_value and assigning the return as value but I still have the same problem.

class Product:
class Extra_1:
value = 5
creates a class Product with an inner class Extra_1. Technically
a class is also an object, and a code like
base_value = 25
final_value = base_value + Product.Extra_1.value
references an attribute of an object Product and an attribute of a
an object Product.Extra_1. But usually classes are templates to create new objects. A code
x = Product()
y = Product()
creates (instantiates) two new objects of class Product. To achieve
what you want, you can define a special method named __init__ (instantiation automatically invokes this method)
and redesign your program to use instantiation. See
https://docs.python.org/3/tutorial/classes.html

Python Class-Level Variables
There are some things I don't understand. First of all, are Extra, Extra1 and Extra_1 the same subclass? Assuming this and that the last two lines of the first code are inside of the Product class, then this is the expected behavior. Remember that those are class level variables, and, as is, they are evaluated just once. No matter what you do with base_value after the second line got interpreted. And this is true also if you make an instance of the Product class, the class-level variable final_value will not change unless you change it directly.
So, if you still want to use the static subclass attribute base_value and the static class attribute value to calculate your final value (I don't know why) you have to make at least final_value an instance level attribute:
class Product:
class Extra_1:
value = 5
def __init__(self):
self.final_value = Product.base_value + Product.Extra_1.value
base_value = 25
This way, every new instance of Product will calculate its final_value according with the actual value of base_value and value.
import myprogram
#First Product instance will calculate its result with the original values
p1 = myprogram.Product()
#Changing the original value
myprogram.Product.Extra_1.value = 25
#After the modification, creating a new instance
p2 = myprogram.Product()
#This will print the original 30
print(p1.final_value)
#This, in the other hand, will print 50
print(p2.final_value)
Python documentation Class and Instance Variables

Related

How are duplicate class names resolved in data classes?

While working with data classes I declared duplicate data class and realized that on creating objects with them, the code worked perfectly fine, as long as I was using the format mentioned on the most recent line from the bottom.
Is duplicate naming really allowed? Can I do some kind of overloading with duplicate data class names? What about inheriting from the data class of the same name?
Just like you can reassign a new value to a name with an assignment statement
x = 1
x = 2
assert x == 2
you can assign a new class object to a name with a class statement.
class X:
pass
old_X = X
class X:
pass
assert old_X is not X
A class statement itself, is a declarative syntax that does three things:
Evaluates its body to define some names
Pass a dict constructed from the names and their values to the metaclass to create a new class
Assigns the return value of the call to the metaclass to the name given by the class statement.
You are just observing the 3rd step.

Class default parameter in python

if I create a class called Car
class Car():
'''car information summary'''
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
self.odometer = 0
I learned that self.odometer=0 allowed me creating a new instance without putting a value for odometer. Every new instance will start with an odometer reading at 0.
But what if I want to create an new instance with a specified odometer reading?
car_1 = Car('Audi', 'S4', '2017', 5000)
It won't allow me to do so. What I am trying to do is to use it like a default value for a function: You don't have to give a value because there is a default, but when you do, you can overwrite the default.
And I do understand that I can revise the attribute afterwards, or write a method to change the attribute. But that's not my concern for now.
Is this a wrong idea to have for OOP?
Python supports the concept of parameters with a default value. You can write a parameter, and give it a default value if it is not specified. In this case it looks like:
class Car():
'''car information summary'''
def __init__(self, make, model, year, odometer=0):
self.make = make
self.model = model
self.year = year
self.odometer = odometer
So in case you call it with three parameters, Python will see that you did not provide a value for the odometer parameter, and it will implicitly assign zero to it.
A point that sometimes causes confusion is that the default values are only evaluated once, at interpretation time, not each time you call the __init__ function.
For example if we write:
def f(x=[]):
return x
Then Python will evaluate [] only once. And each time you do not provide a value for x, you will obtain a reference to the same list (not a new list every time).
Therefore it is usally dangerous to use mutable types (an int is not a mutable type, so we are safe for the odometer=0).
If we thus for instance .append(3) to the outcome of f(), then from now on, f() will return that list ([3]). Which is usually not the intended effect. Some IDEs will give warnings if you use a mutable object.
If you want a new empty list each time x is not provided, a common pattern is:
def f(x=None):
if x is None:
x = []
return x

python - how to share variables between two different instances

I have seen many posts that shows how to access constant value, by inheritance. that's not my case.
I would like to create two instances from two different modules, and be able to share information/variables between them.
two issues:
1. the classes "dont know" each other, because they are on different files, "import"ing them as modules does not help much in this case
2. how can "print_class" instance can access & change values of variable in "cal_class" instance ?
Please note, I am looking for a way to access value from one instance to another. (in my case, there is allot of information (excel tables) to pass between instances, passing parameters through the "main" function is not practical)
Here is my simplify problem:
cal_class_file.py
class cal_class:
def add(self,first,second)
self.result= first + second
print_class_file.py
class print_class:
def print_sum(self):
result_from_cal_class = ?? <=-# How to get the value from local_calculate.result?
print(result_from_cal_class)
main.py
import cal_class_file
import print_class_file
def main():
local_calculate = cal_class_file.cal_class() # create instance
local_print = print_class_file.print_class(); # create instance that reads values from "local_calculate" instance
local_calculate.add(5,6) # calculate addition
local_print.print_sum() # output: 11
local_calculate.add(4,5) # update values in "local_calculate" instance
local_print.print_sum() # output: 9
How can I get the current (Latest) value for "result_from_cal_class" ?
Pass the value in. You have it right there in your client code:
class print_class:
def print_sum(self, result_from_cal_class):
print(result_from_cal_class)
def main():
local_calculate = cal_class_file.cal_class() # create instance
local_print = print_class_file.print_class(); # create instance that reads values from "local_calculate" instance
local_calculate.add(5, 6) # calculate addition
local_print.print_sum(local_calculate.result) # output: 11
local_calculate.add(4, 5) # update values in "local_calculate" instance
local_print.print_sum(local_calculate.result) # output: 9
Output:
11
9
However, if you mean that your client does this:
def main():
# setup elided ...
# actually call to add is done in another function
local_calculate.add(5, 6) # calculate addition
# actually call to print_sum is done somewhere else:
local_print.print_sum() # output: 11
You could define print_class like this:
class print_class:
def __init__(self, calculator):
self.calculator = calculator
def print_sum(self):
print(self.calculator.result)
Then initialise it like this:
def main():
local_calculate = cal_class_file.cal_class() # create instance
local_print = print_class_file.print_class(local_calculate); # create instance that reads values from "local_calculate" instance
Be careful as this is a little like having a global variable: calculate does something, leaving a result, print picks up the last result.
If you have an unexpected call to calculate between the one you want and the print, you will get unexpected results.

How set list value for attribute of class in python

I have created a class distance_neighbor in which one of the attributes is a list of objects of class Crime. That is the value for all attributes I get from database query result.
At first, I have set data_Crime list as the value for attribute **Crime on class distance_neighbor, and I used del to clear data_Crime list after used, so that the data_Crime list can used in the next loop.
This is my code:
conn = psycopg2.connect("dbname='Chicago_crime' user='postgres' host='localhost' password='1234'")
cur= conn.cursor()
minDistance=float(input("Nilai minimum distance : "))
cur.execute("""SELECT id_objek1, objek1, id_objek2, objek2, distance from tb_distance1 where distance<'%f'""" %(minDistance))
class Crime:
def __init__(self, id_jenis, jenis):
self.id_jenis=id_jenis
self.jenis=jenis
class distance_neighbor (Crime):
def __init__(self, distance, **Crime):
self.distance = distance
self.Crime = Crime
data_Crime =[]
data_distance = []
for id_objek1, objek1, id_objek2, objek2, distance in cur.fetchall():
data_Crime.append(Crime(id_objek1,objek1))
data_Crime.append(Crime(id_objek2,objek2))
data_distance.append(distance_neighbor(distance, data_Crime))
del data_Crime[:]
error Message:
data_distance.append(distance_neighbor(distance, data_Crime))
TypeError: __init__() takes exactly 2 arguments (3 given)
I have fixed my code using below answers guys, Thank you
This should be closer to what you want:
class Crime(object):
def __init__(self, id_jenis, jenis):
self.id_jenis=id_jenis
self.jenis=jenis
class DistanceNeighbor(object):
def __init__(self, distance, crimes):
self.distance = distance
self.crimes = crimes
data_distance = []
for id_objek1, objek1, id_objek2, objek2, distance in cur.fetchall():
crimes = [Crime(id_objek1,objek1), Crime(id_objek2,objek2)]
data_distance.append(DistanceNeighbor(distance, crimes))
Classes in Python 2 should always inherit from object. By convention, class names are in CamelCase.
The inheritance of DistanceNeighbor from Crime seems unnecessary. I changed this.
Attributes to instance should be lower case, therefore I used crimes instead of the very confusing reuse of the class name Crime.
This line:
def __init__(self, distance, **Crime):
takes your list of Crime instance apart as separate arguments.
In your case it means the __init__ receives:
distance, data_Crime[0], data_Crime[0]
this causes this error message:
TypeError: init() takes exactly 2 arguments (3 given)
The instantiation of Crime is pretty short. So, instead of the two appends you can create the list of the two Crime instances in one line:
crimes = [Crime(id_objek1,objek1), Crime(id_objek2,objek2)]
Since this creates a new list in each loop, there is no need to delete the list content in each loop, as you did with del data_Crime[:].
You've defined your __init__ method in distance_neighbor as taking arguments (self, distance, **Crime). The ** before Crime tells Python to pack up any keyword arguments you're passed into a dictionary named Crime. That's not what you're doing though. Your call is distance_neighbor(distance, data_Crime) where data_Crime is a list. You should just accept that as a normal argument in the __init__ method:
class distance_neighbor (Crime):
def __init__(self, distance, crime):
self.distance = distance
self.crime = crime
This will mostly work, but you'll still have an issue. The problem is that the loop that's creating the distance_neighbor objects is reusing the same list for all of them (and using del data_Crime[:] to clear the values in between). If you are keeping a reference to the same list in the objects, they'll all end up with references to that same list (which will be empty) at the end of the loop.
Instead, you should create a new list for each iteration of your loop:
for id_objek1, objek1, id_objek2, objek2, distance in cur.fetchall():
data_Crime = [Crime(id_objek1,objek1), Crime(id_objek2,objek2)]
data_distance.append(distance_neighbor(distance, data_Crime))
This will work, but there are still more things that you probably want to improve in your code. To start with, distance_neighbor is defined as inheriting from Crime, but that doesn't seem appropiate since it contains instance of Crime, rather than being one itself. It should probably inherit from object (or nothing if you're in Python 3 where object is the default base). You may also want to change your class and variable names to match Python convention: CamelCase for class names and lower_case_with_underscores for functions, variables and attributes.
def __init__(self, distance, **Crime):
**Crime is a keyword argument, and expects named arguments. You don't need that, remove the asterisks.
Also, rename the argument, it's very confusing that it has the same name as the class:
class distance_neighbor(Crime):
def __init__(self, distance, c):
self.distance = distance
self.Crime = c

Define a constant object in python

I defined a class Factor in the file factor.py:
class Factor:
def __init__(self, var, value):
self.var = var # hold variable names
self.value = value # hold probability values
For convenience and code cleanliness, I want to define a constant variable and be able to access it as Factor.empty
empty = Factor([], None)
What is the common way to do this? Should I put in the class definition, or outside? I'm thinking of putting it outside the class definition, but then I wouln't be able to refer to it as Factor.empty then.
If you want it outside the class definition, just do this:
class Factor:
...
Factor.empty = Factor([], None)
But bear in mind, this isn't a "constant". You could easily do something to change the value of empty or its attributes. For example:
Factor.empty = something_else
Or:
Factor.empty.var.append("a value")
So if you pass Factor.empty to any code that manipulates it, you might find it less empty than you wanted.
One solution to that problem is to re-create a new empty Factor each time someone accesses Factor.empty:
class FactorType(type):
#property
def empty(cls):
return Factor([], None)
class Factor(object):
__metaclass__ = FactorType
...
This adds an empty property to the Factor class. You are safe to do what you want with it, as every time you access empty, a new empty Factor is created.

Categories