Need to Create Class with Default Values for Attributes - python

I need to create a class that that stores tax data as default values, but that can be populated with actual data from tax returns. So for instance, let's say I have three categories: income, statutory adjustments, and tax computation, each which contains several attributes. Ideally, I would like to have three self arguments that I can pass around to parts of a tax calculator. But I would want to be able to pass around each attribute in the self string individually. Right now I have this:
class taxReturn:
def __init__(self):
self.income = [attribute1, attribute2, attribute3]
self.statut = [attribute4, attribute5, attribute6]
self.tax_comp = [attribute7, attribute8, attribute9]
Each attribute would have to be set to a default value that could be passed around, but then later filled in with actual data.
Any help would be appreciated. Thanks!

Why not just define this in the __init__ method?
class taxReturn:
def __init__(self, income = [attribute1, attribute2, attribute3],statut = [attribute4, attribute5, attribute6],tax_comp = [attribute7, attribute8, attribute9]):
self.income = income
self.statut = statut
self.tax_comp = tax_comp
# To create instance of taxReturn:
txR = taxReturn() # income, statut and tax_comp will take the default values defined bellow
//OR
txR = taxReturn(income = NewListOfAttribute1, statut=NewListOfAttribute2,tax_comp=NewListOfAttribute3) # income, statut and tax_comp will take the new attribute defined

Related

How can I use a function to create a persistant list correlated to the given arguments?

I'm writing a script to find the moving average of different stocks. This script runs continuously, looping through my API call to add the current price to a list before averaging it. This works fine, however I'd like to be able to put this into a function to where the only input I need to give it is the name of the stock. I'd like this script to work for as many stocks as I want to specify, at the same time. That's where I run into issues because for each stock I have I need to have an empty list predefined that can hold the pricing information.
I've been trying to use the name of the stock to then create a related list, but as I now understand it it's not a great idea using one variable name to create another variable, so I'm not sure what to do. I believe the usual solution here would be to use a dictionary, but I'm a beginner to programming in general so I haven't figured out how to fit that into my situation. Any help would be greatly appreciated.
def sma(stock_name):
list_exists = stock_name + "_list" in locals() or stock_name + "_list" in globals()
if list_exists:
print()
else:
stock_name + "_list" = [] # Problem line, I would like for this to create a list called stock_name_list
stock_price = requests.get("myapi.com", params={"stock_name": stock_name, "bla bla": "blah"})
stock_name_list.append(stock_price)
When you have an operation based on a version of the data specific to that operation, that is usually a good time to think about using classes. This particular proposed class will encapsulate the name of a stock, the list of data specific to it, and perform sma on it:
class Stock:
n = 10
def __init__(self, name):
self.name = name
self.data = []
def sma(self):
stock_price = requests.get("myapi.com", params={"stock_name": self.stock_name, "bla bla": "blah"})
self.data.append(stock_price)
window = self.data[-n:]
return sum(window) / len(window)
Now you can maintain a dictionary of these objects. Any time you encounter a new stock, you just add an item to the dictionary:
stocks = {}
def sma(name):
stock = stocks.get(name)
if name is None: # None is what get returns when the key is missing
stock = Stock(name)
stocks[name] = stock
return stock.sma()
The nice thing is that you now have a dictionary of named datasets. If you want to add a different statistic, just add a method to the Stock class that implements it.
I defined a global sma function here that calls the eponymous method on the object it finds in your dictionary. You can carry encapsulation to an exterme by making the method perform the action of the function if called statically with a name instead of an instance. For example:
class Stock:
n = 10
named_stocks = {} # This is a class variable that replaces the global stocks
def __init__(self, name):
self.name = name
self.data = []
def sma(self):
if isinstance(self, str):
self = Stock.get_stock(self)
stock_price = requests.get("myapi.com", params={"stock_name": self.stock_name, "bla bla": "blah"})
self.data.append(stock_price)
window = self.data[-n:]
return sum(window) / len(window)
#classmethod
def get_stock(cls, name):
stock = cls.named_stocks.get(name)
if stock is None:
stock = cls(name)
cls.named_stocks[name] = stock
return stock
Now that there is a check for isinstance(self, str), you can call the sma method in one of two ways. You can all it directly on an instance, which knows its own name:
aapl = Stock('AAPL')
aapl.sma()
OR
Stock.get_stock('AAPL').sma()
Alternatively, you can call it on the class, and pass in a name:
Stock.sma('AAPL')
use defaultdict
from collections import defaultdict
stock_name_to_stock_prices = defaultdict(list)
stock_name_to_stock_prices['STOCK_NAME'].append(123.45)

Python,unable to call initial arguments outside the class

The below python class have empty dictionary as initial arguments, after calling
createAccount() outside class it successfully add data to dictionary but I can't access dictionary outside class.
What changes shall I make in the below code to access the newly created account details ?
*Please note that my error occurs in the last line of the code *
class SavingsAccount():
def __init__(self):
self.savingsAccounts = {}
def createAccount(self, name, initialDeposit):
print()
self.accountNumber = int(12345)
self.savingsAccounts[self.accountNumber] = [name, initialDeposit]
print("Account creation has been successful. Your account number is ", self.accountNumber)
SavingsAccount().createAccount(name = 'a',initialDeposit=4)
print(SavingsAccount().savingsAccounts[12345]) # getting error here
You should initialize your object using __init__,
class SavingsAccount:
def __init__(self, name, initial_deposit):
self.accountNumber = 12345
self.savingsAccounts = {self.accountNumber : [name, initial_deposit] }
print("Account creation has been successful. Your account number is ", self.accountNumber)
saving_account = SavingsAccount(name='a', initial_deposit=4)
print(saving_account.savingsAccounts)
Also, most of the Pythonistas prefer snake_casing while naming variables.
You are creating a new instance of SavingsAccount with every call. After you call to createAccount completes, that instance is garbage-collected, as there are no references to it stored anywhere.
s = SavingsAccount()
s.createAccount(name='a', initialDeposit=4)
print(s.savingsAccounts[12345])
(See Taohidul Islam's answer for how you should be defining the class, though.)
The line that gives the error does this actions:
Calls SavingsAccount.init() to create the object
Asks for the item 12345 in the dictionary (that whas just created so it's empty)
You should structure your code in a different way. You should have a list of accounts or similar that is unique, and then insert in it the accounts you create.
Must first initialize an instance of your SavingsAccount class
#initialize savings account object
s = SavingsAccount()
#call created account method
s.createAccount(name="a", initialDeposit=4)
#print the account
print(s.savingsAccounts[12345])
Although your datastructure is confusing, why not have one instance of a savings account object represent an individuals account? Then you could just assign member variables for values you want to track.
class SavingsAccount:
def __init__(self, name, initial_deposit):
self.account_name = name
self.bal = initial_deposit
def deposit(self, val):
self.bal += val
def return_account(self):
return self.__dict__
Now you can use it more simplistically
s = SavingsAccount("name", 500)
s.deposit(500)
acc = s.return_account()
print(acc)
>> {"account_name": "name", "bal": 1000}

Recommended Python design for capturing changes to class members

I'm looking for a design pattern suited to Python that will address the following problem:
Assuming a class Company that has a member employees, which is a list that will contain any number of Employees.
What I would like to capture is when one of the Employee's member variables (let's say salary) changes, that change is reflected in the "owning" Company (let's say total_salary). Let's also assume that total_salary is very expensive to calculate and we only want to do that calculation whenever any employee's salary is changed and not whenever we access it as a property.
> c = Company()
> print(c.total_salary)
0
> c.employees.append(Employee())
> print(c.total_salary)
0
c.employees[0].salary = 100 # update total_salary for c now
> print(c.total_salary)
100
The obvious way is by making sure that there is a reference from each Employee back to its owning Company. What I'm curious is whether there is a good way to use a getter, setter or something else in Company to ensure that I can capture a salary change to any element in employees, so that I can immediately update total_salary. Crucially, we should avoid this recalculation when another member is updated (such as name).
class Company:
def __init__(self, ....):
self.employees = []
self.total_salary = 0
def add_employe(self, employe):
self.employees.append(employe)
self.total_salary += employe.salary
def remove_employe(self, employe):
self.employees.remove(employe)
self.total_salary -= employe.salary
def update_employe(self, employe):
for e in self.employees:
if not employe.id == e.id:
continue
e.name = employe.name
# ... update more data here
if employe.salary != e.salary: # salary suffered changes, update it
self.total_salary -= e.salary # subtract the older
self.total_salary += employe.salary # sum up the new
e.salary = employe.salary # update it
class Employee:
_id = itertools.count(start=1)
def __init__(self, ...):
self.id = next(Employee._id)
# ...
You want update total_salary only if the employe.salary suffered any changes. The responsible for this is in update_employe method.
Also an implementation of some sort of id to Employee is useful.
If you're looking for getter/setter control, look into Python property. You can use this pattern to catch when variables are set, in order to implement the auto-update feature.
I think there are a couple of reasonable ways to do what you're asking. One is to have the company dynamically compute its totals each time they're requested. The other approach is for the Employee to know the company it belongs to, and for it to update the company's totals whenever it's own information changes.
I think the first approach is easier, since the Employee instances doesn't need to know anything about the Company, and you don't to do anything special when an Employee is updated. The downside is that if you have a company with many employees and request its totals often, it may be slow, since it needs to iterate over all the employees each time. Here's how I'd implement it, using a property:
class Company:
def __init__(self):
self.employees = []
#property
def total_salary(self):
return sum(e.salary for e in self.employees)
If you go with the second approach, you'd also use a property, but you'd put it in the Employee class, so it can detect changes being made to the salary attribute:
class Company:
def __init__(self):
self.employees = []
self.total_salary = 0
class Employee:
def __init__(self, employer):
self.employer = employer
self._salary = 0
#property
def salary(self):
return self._salary
#salary.setter
def salary(self, value):
self.employer.total_salary += value - self._salary
self._salary = value
To make this work in a more complicated system, you'd probably want a bunch of other methods, like one that adds an Employee with a salary already set to a Company (currently you must add the Employee to the company first, then update it's salary, or the totals will be wrong).

How to access a class object and its attributes from user input?

I just started programming and I decided to use Python for my first attempts at coding, and I am now practicing with classes and objects.
I apologize if the question I am about to ask has been asked before, but I can't seem to find answers anywhere, so here it goes.
I have a file that contains a class. Below the full code I have written :
#class file
#class prodotti refers to "register" with products in stock and their prices
class Prodotti(): #class Prodotti() contains products from register and their relative specs
def __init__(self, nome="", #name of product
prezzo=0, #product price
quantità=0,): #stock quantity of product
self.nome=nome
self.prezzo=prezzo
self.quantità=quantità
def newproduct(self): #method appends new product and its specs to the end of this file
name=input("Inserire nuovo prodotto: ")
f=open("cassa3.py", "a")
f.write(name + "=Prodotti(nome='" + name + "', ")
price=input("Inserire prezzo prodotto: ")
f.write("prezzo=" + price + ", quantità=0)\n")
f.close()
def tellprice(self): #method should return price of object
inp=input("Di quale prodotto vuoi conoscere il prezzo? ") #asks user which product they want to know the price of
if inp=Prodotti():
print(inp.prezzo)
#class objects
#user can insert new products that are saved below
tortino=Prodotti(nome="Tortino al cioccolato", prezzo=3.4, quantità=0)
muffincioccolato =Prodotti(nome="Muffin al cioccolato", prezzo=1.8, quantità=0)
cupcake=Prodotti(nome='cupcake', prezzo=2, quantità=0)
In another file, saved in the same directory, I have the main program:
from cassa3 import Prodotti #file cassa3.py in same directory as this file
if __name__=="__main__":
P=Prodotti()
P.tellprice()
As you may tell from the code above, what I want method tellprice() to do is to ask the user what product they want to know the price of.
However, I just don't know how to make the user input correspond to a class object, so that I can access its attributes.
Can someone explain how i could manage to do that?
Thanks in advance.
Before you will be able to solve this issue, you will need to fix the design problem you have.
Your comment says # class Prodotti() contains products from register and their relative specs but it is not quite true. This class contains a single product with its name, price and quantity.
You will need to define another class (perhaps Register) that will actually store a list (or dictionary if product names are unique for efficient lookup, or whatever) of products (instances of Prodotti).
The tellprice method currently makes no sense. It simply creates a new instance of Prodotti and the if condition will never be True.
Also, it is highly suggested to use English names in code.
Consider the below example as a general guide:
class Product:
def __init__(self, name, price, quantity):
self.name = name
self.price = price
self.quantity = quantity
# (... some other methods ... )
class Register:
def __init__(self, products):
# this will store the products in a dictionary with products names as keys
# and Product instances as values for an efficient look up by tell_price
self.products = {product.name: product for product in products}
def tell_price(self):
name = input('Which product would you like to know the price of?')
# this will raise KeyError if user inputs a non-existing product name
# and should be caught, or use .get(...) instead
return self.products[name].price
apple = Product('apple', 1, 10)
banana = Product('banana', 2, 2)
register = Register([apple, banana])
print(register.tell_price())
# Which product would you like to know the price of?
>> apple
# 1
I wouldn't make your tellprice include user input.
def tellprice(self): #method should return price of object
return self.price
Then in main (this is significantly simplified):
inp = input("Di quale prodotto vuoi conoscere il prezzo? ")
print(inp.tellprice)
Obviously this assumes they put in the correct product name, so some way of indicating to the user that they're incorrect might be useful

Accessing elements of lists, and calling their fuctions

Here is Customer class:
class Customer:
def __init__(self, timestamp, cid, item_count):
self.time_stamp = timestamp
self.customer_name = cid
self.item_count = item_count
def checkout(self, new_timestamp):
self.time_stamp = new_timestamp
def get_cus_name(self):
return self.customer_name
If I create an empty list of Customer objects like:
customers = [Customer]
And then somewhere else I try to call Customer methods in a loop like:
def checkout_customer(self, cid):
for cus in self.customers:
if cus.get_cus_name == cid:
cus.checkout(self.cur_num_customers + 7)
why do I get an error when I try to call cus.checkout? My ide tells me that it expects a Customer but got an int. Why doesn't it pass itself into the 'self' arg here?
However if I just create a Customer object and directly call its methods, it works fine:
def foo(self):
cus = Customer(1,'pop',2)
cus.checkout(23)
This is my first time learning python, and ive been stuck trying to figure out lists, and accessing its members. Perhaps my initialization of self.custormers = [Customer] is incorrect?
EDIT:
In my constructor of tester class I create an empty list like this:
self.customer = [Customer]
I am able to add customers no problem:
def add_custormer(self, customer):
self.customers.append(customer)
My problem is not adding customers, but accessing their methods once they are in a list. Doing something like this self.customers[0].checkout(1,'pop',2) gives me an error "Expected type 'Customer' got int".
I am not sure of the class where checkout_customer lives but I am assuming you declare the list self.customers somewhere in it.
self.costumers = []
If you intend to add an element Customer to the list you should use something like: self.customers.append(Customer(x,y,z)) since you want to add a new customer to the list and when doing so you are required to initialize the Customer class.
I didn't try the code but I believe something like this should work:
def foo(self):
self.customers.append(Customer(1,'pop',2))
self.checkout_customers(23)

Categories