comparing a number with a prefix in a class in python - python

I am working on a small module.
I have a class called pricelist. It contains the attributes prefix and price.
class pricelist:
def setPrefix(self,prefix):
self.prefix = prefix
def setPrice(self,price):
self.price = price
def getPrefix(self):
return self.prefix
def getPrice(self):
return self.price
def getPrefixLength(self):
return len(self.prefix)
I have created a list of pricelist objects.
now i want to perform the operation, where when i give a phone number as input, i want to find prefix of the number and the corresponding price related to the prefix.
Example:
prefix price
46 2.0
44 3.0
.
.
.
.
when i give the input say "46 7223232323", it should return the price corresponding to it.
I am new to python and python classess, so can you please help me out with the logic of this

Keep the prefixes and prices in a dict:
self.data = {"46": 2.0,"44":3.0}
inp = "46 7223232323"
get_pre = inp.split()[0]
print (self.data.get(get_pre))
2.0
class Pricelist:
def __init__(self):
self.data = {"46": 2.0,"44":3.0}
p = Price_list()
inp = "46 7223232323"
get_pre = inp.split()[0]
print (p.data.get(get_pre,"Sorry that is an invalid prefix")) # if the prefix does not exist, it will output "Sorry that is an invalid prefix"
You can access attributes directly without getters and setters:
class Price_list:
def __init__(self,prefix,price):
self.prefix = prefix
self.price = price
self.pre_len = len(self.prefix)
p = Price_list("46", "4.99")
print (p.prefix, p.price, p.pre_len)
You can use data as a class attribute and add all instance attributes to it:
class Price_list:
data = {}
def __init__(self, prefix, price):
self.prefix = prefix
self.price = price
self.pre_len = len(self.prefix)
p = Price_list("46", "4.99")
p.data[p.prefix] = [p.price]
p1 = Price_list("44", "3.99")
p1.data[p1.prefix] = [p1.price]

Related

Python 3.8: TypeError: 'dict' object is not callable when trying to get data from a file

ran into a roadblock during a tutorial. I have no idea on how to solve this. I'm trying to use data from a csv file. When I try accessing the data with the current code commented out (see below), it works by outputting existing data.
However, it immediately errors when I add back in the commented code, with the error mentioned in the title. Could someone look at this and tell me what I've done wrong?
import csv
class item:
pay_rate = 0.8 #the payrate after 20% discount
all = []
def __init__(self, name: str, price: float, quantity: int = 0):
#run validations of received arguments
assert price >= 0, f"price {price} !> 0"
assert quantity >= 0, f"quantity {quantity} !> 0"
#assign to self object
print(f"an instance created: {name}")
self.name = name
self.price = price
self.quantity = quantity
#actions to execute
item.all.append(self)
def calculate_total_price(self):
return self.price * self.quantity
def apply_discount(self):
self.price = self.price * self.pay_rate
#classmethod
def instantiate_from_csv(cls):
with open('items.csv', 'r') as f:
reader = csv.DictReader(f)
items = list(reader)
for item in items:
item(
name=item.get('name'),
price=float(item.get('price')),
quantity=int(item.get('quantity')),
)
def __repr__(self):
return f"item({self.name},{self.price}, {self.quantity})"
item.instantiate_from_csv()
print(item.all)
By naming several things item, you managed to confuse even yourself.
Below is your code with things sensibly renamed and the problem fixed. Take a look at the comments to see what was changed:
import csv
# renamed to AllItems, because that's what you end up using it for
# you're mixing up an instance and a container of instances here and
# it gets you the worst of both worlds
class AllItems:
pay_rate = 0.8
all = []
def __init__(self, name: str, price: float, quantity: int = 0):
assert price >= 0, f"price {price} !> 0"
assert quantity >= 0, f"quantity {quantity} !> 0"
print(f"an instance created: {name}")
self.name = name
self.price = price
self.quantity = quantity
# you can do this, but you in some cases you want to use
# self.all.append instead, which will still access the class
# attribute
AllItems.all.append(self)
def calculate_total_price(self):
return self.price * self.quantity
def apply_discount(self):
# this is dubious - can you apply the same discount many times?
self.price = self.price * self.pay_rate
# renamed to 'load', sure you instantiate, but you don't return the
# instance, you're really just abusing the class as a container
#classmethod
def load_from_csv(cls, fn):
# it seems to make more sense to be able to use different csvs?
with open(fn, 'r') as f:
reader = csv.DictReader(f)
item_dr = list(reader)
for item_dict in item_dr:
# your problem was right here, you referred to `item()` where
# you meant to refer to the class, which is already passed as `cls`
cls(
name=item_dict.get('name'),
price=float(item_dict.get('price')),
quantity=int(item_dict.get('quantity')),
)
def __repr__(self):
# I'm not fixing this, but see remark below
return f"item({self.name},{self.price}, {self.quantity})"
AllItems.load_from_csv('items.csv')
# confusing double `.all` here, didn't bother improving the naming
# considering that it's the result of the design decision you made
print(AllItems.all)
For an items.csv with this:
name,price,quantity
banana,10,12
apple,4,6
pear,7,6
Output:
an instance created: banana
an instance created: apple
an instance created: pear
[item(banana,10.0, 12), item(apple,4.0, 6), item(pear,7.0, 6)]
Note that this fixes your issue but still leaves your code with some serious problems:
You're using the class as a container; that works, but it's not a great way of using a class like this and it certainly won't be what others expect. You're robbing yourself of all of the features a better container like a list would get you, like the it being iterable, or a dict, which would allow easy lookup of products (items). Consider creating an Item class that is just the Item and an Items (or something better) class that contains items and has additional operations.
Note that your representation __repr__ really doesn't give you a representation, that's not how someone could recreate the class at all and it's probably more of a __str__
Here's an example of what I'm suggesting:
import csv
class Item:
pay_rate = 0.8
def __init__(self, name: str, price: float, quantity: int = 0):
assert price >= 0, f"price {price} !> 0"
assert quantity >= 0, f"quantity {quantity} !> 0"
self.name = name
self._price = price
self.quantity = quantity
self.discount = None
#property
def price(self):
if self.discount is None:
return self._price
else:
return self._price * self.discount
#property
def total_price(self):
return self.price * self.quantity
def apply_pay_rate(self):
self.discount = self.pay_rate
#classmethod
def item_list_from_csv(cls, fn):
with open(fn, 'r') as f:
reader = csv.DictReader(f)
item_dr = list(reader)
return [cls(
name=item_dict.get('name'),
price=float(item_dict.get('price')),
quantity=int(item_dict.get('quantity')),
) for item_dict in item_dr]
def __repr__(self):
return f"Item({self.name}, {self.price}, {self.quantity})"
item_list = Item.item_list_from_csv('items.csv')
print(item_list)
print([item.total_price for item in item_list])
Output:
[Item(banana,10.0, 12), Item(apple,4.0, 6), Item(pear,7.0, 6)]
[120.0, 24.0, 42.0]

How to use __str__ function to return a string representation of a class

I have been tasked with creating an inventory manager in python, and one of the required is to define the following function within the class:
__str__ - This function returns a string representation of a
class.
The class refers to a file (inventory.txt) which has the following format:
Country,Code,Product,Cost,Quantity
my code thus far is as follows:
# Creating the class:
class Shoes():
# Constructor:
def __init__(self,country,code,product,cost,quantity):
self.country = country
self.code = code
self.product = product
self.cost = cost
self.quantity = quantity
# Methods:
def get_cost():
inventory = open('inventory.txt','r')
inventory_list = inventory.readlines()
code = input("What is the code of the product:")
for line in inventory_list:
split_lines = line.split(",")
if code == split_lines[1]:
print("This product costs R{}".format(split_lines[3]))
inventory.close()
def get_quantity():
inventory = open('inventory.txt','r')
inventory_list = inventory.readlines()
code = input("What is the code of the product:")
for line in inventory_list:
split_lines = line.split(",")
if code == split_lines[1]:
print("There are {} units in inventory".format(split_lines[4]))
inventory.close()
def __str__(self):
pass
I haven't come across the str so I really am not sure how it works, and how to use it. Any advice would be greatly appreciated.
Here is an example:
def __str__(self):
return f'{self.self.country},{self.self.code},{self.self.product}, {self.cost},{self.quantity })'
This way, when you assign values to a class, you can print its string representation
print(new_shoe)
More info here
https://www.pythontutorial.net/python-oop/python-str/

Get attributes of class item from string associated with class item

I have a niche problem. I create a class classA, with attributes name and number. Once a class item is created, its name is stored as a string in a list, namesList. Later in the code, namesList prints and the user can enter a string input. If that input matches a string in namesList, I want the program to print the number attribute associated with that class item. How should I do this?
ClassA is just a class. You tried to reference it in the last line which leads to an error. Instead of doing that appending the object to the list would be better because then you can individually get the value back from the object while searching in the array.
namesList = []
class classA:
def __init__(self, name, number):
self.name = name
self.number = number
namesList.append(self)
def getNumber(self):
return self.number
def getName(self):
return self.name
example = 'c'
classA(example, 5)
userChoice = input('Which name do you need the number for?')
for name in namesList:
if name.getName() == userChoice:
nameIndex = namesList.index(name)
print(nameIndex)
print('The current price for', name.getNumber())
I think what you're trying to do is this:
namesList = []
class classA:
def __init__(self, name, number):
self.name = name
self.number = number
namesList.append(self.name)
example_Class = classA('example', 5)
userChoice = input('Which name do you need the number for?')
for name in namesList:
if name == userChoice:
print('The number is', example_Class.number)
You have to set classA('example', 5) equal to a variable example_Class and then if you want to access the number value stored it's just example_Class.number
EDIT
This code ought to work regardless of how many class items there are:
class Iterator(type):
def __iter__(cls):
return iter(cls.namesList)
class ClassA(metaclass=Iterator):
namesList = []
def __init__(self, name, number):
self.name = name
self.number = number
self.namesList.append(self)
example_Class1 = ClassA('one', 8)
example_Class2 = ClassA('two', 7)
example_Class3 = ClassA('three', 6)
userChoice = input('Which name do you need the number for?')
for class_name in ClassA:
if class_name.name == userChoice:
print('The number is', class_name.number)

Python object creating a group with 3 members of aggregation relationship

I had an assignment to create a python code using class to create a group with 3 members (aggregation relationship). This is my code so far:
class Member:
def __init__(self,name,age):
self.name = name
self.age = age
def getInfo(self):
memberInfo = "Name: " + str(self.name) + "." + "Age: " + str(self.age)
return memberInfo
class Group:
def __init__(self,name):
self.name = name
self.memlist = []
def addMember(self,member):
self.memlist.append(member)
def getInfo(self):
info = "Member List: \n"
for i in range(len(self.memlist)):
info += self.memlist[i].getInfo() + "\n"
print(info)
break
mem1 = Member("Chi",20)
mem2 = Member("Bach",7)
mem3 = Member("Gen", 22)
group1 = Group("Siblings")
group1.addMember(mem1)
group1.addMember(mem2)
print(group1.getInfo())
print(mem2.getInfo())
print(group1.memList)
But it has shown an error: AttributeError: 'Group' object has no attribute 'memList'. Is there anything I can do to fix this?
I wrote little function for listing members and their ages.
class member:
def __init__(self, name, age):
self.name = name
self.age = age
def member_Info(self):
memberInfo = f"Name: {str(self.name)}-->Age: {str(self.age)}"
return memberInfo
class Group:
def __init__(self, name):
self.name = name
self.memlist = []
def addMember(self, name):
self.memlist.append(name)
def getInfo(self):
for i in range(len(self.memlist)):
info = self.memlist[i].member_Info() + "\n"
print(info)
This all_members function is basically getting the information stored in the member class and return to list. I print using memlist in Group but it didn't work out so I made a new list using all_member function and get information from memlist in group1 with the code that you used for getting information in memlist at group1.getInfo .
def all_members():
all_mems = []
for i in range(len(group1.memlist)):
all_mems.append(group1.memlist[i].member_Info())
print(all_mems)
mem1 = member("Chi", "20")
mem2 = member("Bach", "7")
mem3 = member("Gen", "22")
group1 = Group("Siblings")
group1.addMember(mem1)
group1.addMember(mem2)
group1.addMember(mem3)
print(group1.getInfo())
print(mem2.member_Info() + "\n")
print(all_members())
I guess this isn't the best answer you can get but I think it will work and also I learn many things while trying to correct it so thank you for posting that.
change
print(group1.memList)
to
print(group1.memlist)

Return class instance instead of creating a new one if already existing

I defined a class named Experiment for the results of some lab experiments I am conducting. The idea was to create a sort of database: if I add an experiment, this will be pickled to a db before at exit and reloaded (and added to the class registry) at startup.
My class definition is:
class IterRegistry(type):
def __iter__(cls):
return iter(cls._registry)
class Experiment(metaclass=IterRegistry):
_registry = []
counter = 0
def __init__(self, name, pathprotocol, protocol_struct, pathresult, wallA, wallB, wallC):
hashdat = fn.hashfile(pathresult)
hashpro = fn.hashfile(pathprotocol)
chk = fn.checkhash(hashdat)
if chk:
raise RuntimeError("The same experiment has already been added")
self._registry.append(self)
self.name = name
[...]
While fn.checkhash is a function that checks the hashes of the files containing the results:
def checkhash(hashdat):
for exp in cl.Experiment:
if exp.hashdat == hashdat:
return exp
return False
So that if I add a previously added experiment, this won't be overwritten.
Is it possible to somehow return the existing instance if already existant instead of raising an error? (I know in __init__ block it is not possible)
You can use __new__ if you want to customize the creation instead of just initializing in newly created object:
class Experiment(metaclass=IterRegistry):
_registry = []
counter = 0
def __new__(cls, name, pathprotocol, protocol_struct, pathresult, wallA, wallB, wallC):
hashdat = fn.hashfile(pathresult)
hashpro = fn.hashfile(pathprotocol)
chk = fn.checkhash(hashdat)
if chk: # already added, just return previous instance
return chk
self = object.__new__(cls) # create a new uninitialized instance
self._registry.append(self) # register and initialize it
self.name = name
[...]
return self # return the new registered instance
Try to do it this way (very simplified example):
class A:
registry = {}
def __init__(self, x):
self.x = x
#classmethod
def create_item(cls, x):
try:
return cls.registry[x]
except KeyError:
new_item = cls(x)
cls.registry[x] = new_item
return new_item
A.create_item(1)
A.create_item(2)
A.create_item(2) # doesn't add new item, but returns already existing one
After four years of the question, I got here and Serge Ballesta's answer helped me. I created this example with an easier syntax.
If base is None, it will always return the first object created.
class MyClass:
instances = []
def __new__(cls, base=None):
if len(MyClass.instances) == 0:
self = object.__new__(cls)
MyClass.instances.append(self)
if base is None:
return MyClass.instances[0]
else:
self = object.__new__(cls)
MyClass.instances.append(self)
# self.__init__(base)
return self
def __init__(self, base=None):
print("Received base = %s " % str(base))
print("Number of instances = %d" % len(self.instances))
self.base = base
R1 = MyClass("apple")
R2 = MyClass()
R3 = MyClass("banana")
R4 = MyClass()
R5 = MyClass("apple")
print(id(R1), R1.base)
print(id(R2), R2.base)
print(id(R3), R3.base)
print(id(R4), R4.base)
print(id(R5), R5.base)
print("R2 == R4 ? %s" % (R2 == R4))
print("R1 == R5 ? %s" % (R1 == R5))
It gives us the result
Received base = apple
Number of instances = 2
Received base = None
Number of instances = 2
Received base = banana
Number of instances = 3
Received base = None
Number of instances = 3
Received base = apple
Number of instances = 4
2167043940208 apple
2167043940256 None
2167043939968 banana
2167043940256 None
2167043939872 apple
R2 == R4 ? True
R1 == R5 ? False
Is nice to know that __init__ will be always called before the return of the __new__, even if you don't call it (in commented part) or you return an object that already exists.

Categories